morpheus-cli 3.6.28 → 3.6.29
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.
- checksums.yaml +4 -4
- data/lib/morpheus/api/api_client.rb +16 -0
- data/lib/morpheus/api/cloud_folders_interface.rb +47 -0
- data/lib/morpheus/api/cloud_resource_pools_interface.rb +47 -0
- data/lib/morpheus/api/network_types_interface.rb +26 -0
- data/lib/morpheus/api/reports_interface.rb +77 -0
- data/lib/morpheus/api/security_group_rules_interface.rb +6 -0
- data/lib/morpheus/api/security_groups_interface.rb +21 -15
- data/lib/morpheus/cli.rb +3 -0
- data/lib/morpheus/cli/accounts.rb +1 -1
- data/lib/morpheus/cli/apps.rb +2 -2
- data/lib/morpheus/cli/archives_command.rb +18 -18
- data/lib/morpheus/cli/blueprints_command.rb +1 -1
- data/lib/morpheus/cli/boot_scripts_command.rb +6 -6
- data/lib/morpheus/cli/cli_command.rb +4 -0
- data/lib/morpheus/cli/cloud_datastores_command.rb +58 -20
- data/lib/morpheus/cli/cloud_folders_command.rb +463 -0
- data/lib/morpheus/cli/cloud_resource_pools_command.rb +707 -0
- data/lib/morpheus/cli/clouds.rb +2 -0
- data/lib/morpheus/cli/hosts.rb +33 -8
- data/lib/morpheus/cli/instances.rb +79 -54
- data/lib/morpheus/cli/library_option_lists_command.rb +1 -1
- data/lib/morpheus/cli/library_option_types_command.rb +1 -1
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +11 -2
- data/lib/morpheus/cli/monitoring_contacts_command.rb +1 -1
- data/lib/morpheus/cli/monitoring_incidents_command.rb +1 -1
- data/lib/morpheus/cli/network_services_command.rb +7 -3
- data/lib/morpheus/cli/networks_command.rb +164 -63
- data/lib/morpheus/cli/option_types.rb +16 -15
- data/lib/morpheus/cli/policies_command.rb +76 -9
- data/lib/morpheus/cli/preseed_scripts_command.rb +2 -2
- data/lib/morpheus/cli/remote.rb +26 -28
- data/lib/morpheus/cli/reports_command.rb +594 -0
- data/lib/morpheus/cli/security_group_rules.rb +5 -1
- data/lib/morpheus/cli/security_groups.rb +882 -45
- data/lib/morpheus/cli/tasks.rb +158 -23
- data/lib/morpheus/cli/tenants_command.rb +1 -1
- data/lib/morpheus/cli/users.rb +1 -1
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +9 -2
@@ -289,7 +289,7 @@ class Morpheus::Cli::BlueprintsCommand
|
|
289
289
|
payload.deep_merge!(passed_options) unless passed_options.empty?
|
290
290
|
|
291
291
|
if passed_options.empty?
|
292
|
-
print_red_alert "Specify
|
292
|
+
print_red_alert "Specify at least one option to update"
|
293
293
|
puts optparse
|
294
294
|
return 1
|
295
295
|
end
|
@@ -36,7 +36,7 @@ class Morpheus::Cli::BootScriptsCommand
|
|
36
36
|
options = {}
|
37
37
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
38
38
|
opts.banner = subcommand_usage()
|
39
|
-
build_common_options(opts, options, [:list, :json, :dry_run])
|
39
|
+
build_common_options(opts, options, [:list, :json, :dry_run, :remote])
|
40
40
|
end
|
41
41
|
optparse.parse!(args)
|
42
42
|
connect(options)
|
@@ -94,7 +94,7 @@ class Morpheus::Cli::BootScriptsCommand
|
|
94
94
|
options = {}
|
95
95
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
96
96
|
opts.banner = subcommand_usage("[boot-script]")
|
97
|
-
build_common_options(opts, options, [:json, :dry_run])
|
97
|
+
build_common_options(opts, options, [:json, :dry_run, :remote])
|
98
98
|
end
|
99
99
|
optparse.parse!(args)
|
100
100
|
if args.count < 1
|
@@ -150,7 +150,7 @@ class Morpheus::Cli::BootScriptsCommand
|
|
150
150
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
151
151
|
opts.banner = subcommand_usage("[fileName]")
|
152
152
|
build_option_type_options(opts, options, add_boot_script_option_types(false))
|
153
|
-
build_common_options(opts, options, [:options, :json, :dry_run, :quiet])
|
153
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :quiet, :remote])
|
154
154
|
end
|
155
155
|
optparse.parse!(args)
|
156
156
|
connect(options)
|
@@ -210,7 +210,7 @@ class Morpheus::Cli::BootScriptsCommand
|
|
210
210
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
211
211
|
opts.banner = subcommand_usage("[boot-script] [options]")
|
212
212
|
build_option_type_options(opts, options, update_boot_script_option_types(false))
|
213
|
-
build_common_options(opts, options, [:options, :json, :dry_run])
|
213
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
214
214
|
end
|
215
215
|
optparse.parse!(args)
|
216
216
|
if args.count < 1
|
@@ -230,7 +230,7 @@ class Morpheus::Cli::BootScriptsCommand
|
|
230
230
|
#puts "parsed params is : #{params.inspect}"
|
231
231
|
params = params.select {|k,v| params[k].to_s != "" }
|
232
232
|
if params.empty?
|
233
|
-
print_red_alert "Specify
|
233
|
+
print_red_alert "Specify at least one option to update"
|
234
234
|
puts optparse
|
235
235
|
return 1
|
236
236
|
end
|
@@ -279,7 +279,7 @@ class Morpheus::Cli::BootScriptsCommand
|
|
279
279
|
options = {}
|
280
280
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
281
281
|
opts.banner = subcommand_usage("[boot-script]")
|
282
|
-
build_common_options(opts, options, [:account, :auto_confirm, :json, :dry_run])
|
282
|
+
build_common_options(opts, options, [:account, :auto_confirm, :json, :dry_run, :remote])
|
283
283
|
end
|
284
284
|
optparse.parse!(args)
|
285
285
|
|
@@ -197,6 +197,10 @@ module Morpheus
|
|
197
197
|
# unless command_name == "shell"
|
198
198
|
# option_keys << :quiet unless option_keys.include?(:quiet)
|
199
199
|
# end
|
200
|
+
|
201
|
+
# ensure commands can always access options[:options], until we can deprecate it...
|
202
|
+
options[:options] ||= {}
|
203
|
+
|
200
204
|
while (option_key = option_keys.shift) do
|
201
205
|
case option_key.to_sym
|
202
206
|
|
@@ -33,15 +33,23 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
33
33
|
options = {}
|
34
34
|
params = {}
|
35
35
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
36
|
-
opts.banner = subcommand_usage("
|
36
|
+
opts.banner = subcommand_usage("[cloud]")
|
37
37
|
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
38
38
|
cloud_id = val
|
39
39
|
end
|
40
|
+
opts.add_hidden_option('-c') # prefer args[0] for [cloud]
|
40
41
|
build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :json, :dry_run, :remote])
|
41
42
|
opts.footer = "List datastores for a cloud." + "\n" +
|
42
|
-
"
|
43
|
+
"[cloud] is required. This is the name or id of the cloud."
|
43
44
|
end
|
44
45
|
optparse.parse!(args)
|
46
|
+
if args.count == 1
|
47
|
+
cloud_id = args[0]
|
48
|
+
elsif args.count == 0 && cloud_id
|
49
|
+
# support -c
|
50
|
+
else
|
51
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
52
|
+
end
|
45
53
|
connect(options)
|
46
54
|
begin
|
47
55
|
# load cloud
|
@@ -111,22 +119,27 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
111
119
|
|
112
120
|
def get(args)
|
113
121
|
cloud_id = nil
|
122
|
+
datastore_id = nil
|
114
123
|
options = {}
|
115
124
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
116
|
-
opts.banner = subcommand_usage("[datastore]")
|
125
|
+
opts.banner = subcommand_usage("[cloud] [datastore]")
|
117
126
|
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
118
127
|
cloud_id = val
|
119
128
|
end
|
129
|
+
opts.add_hidden_option('-c') # prefer args[0] for [cloud]
|
120
130
|
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
121
131
|
opts.footer = "Get details about a datastore." + "\n" +
|
122
|
-
"[
|
123
|
-
"
|
132
|
+
"[cloud] is required. This is the name or id of the cloud." + "\n" +
|
133
|
+
"[datastore] is required. This is the name or id of a datastore."
|
124
134
|
end
|
125
135
|
optparse.parse!(args)
|
126
|
-
if args.count
|
127
|
-
|
128
|
-
|
129
|
-
|
136
|
+
if args.count == 2
|
137
|
+
cloud_id = args[0]
|
138
|
+
datastore_id = args[1]
|
139
|
+
elsif args.count == 1 && cloud_id
|
140
|
+
datastore_id = args[0]
|
141
|
+
else
|
142
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
130
143
|
end
|
131
144
|
connect(options)
|
132
145
|
begin
|
@@ -139,14 +152,14 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
139
152
|
return 1 if cloud.nil?
|
140
153
|
@cloud_datastores_interface.setopts(options)
|
141
154
|
if options[:dry_run]
|
142
|
-
if
|
143
|
-
print_dry_run @cloud_datastores_interface.dry.get(cloud['id'],
|
155
|
+
if datastore_id.to_s =~ /\A\d{1,}\Z/
|
156
|
+
print_dry_run @cloud_datastores_interface.dry.get(cloud['id'], datastore_id.to_i)
|
144
157
|
else
|
145
|
-
print_dry_run @cloud_datastores_interface.dry.list(cloud['id'], {name:
|
158
|
+
print_dry_run @cloud_datastores_interface.dry.list(cloud['id'], {name:datastore_id})
|
146
159
|
end
|
147
160
|
return
|
148
161
|
end
|
149
|
-
datastore = find_datastore_by_name_or_id(cloud['id'],
|
162
|
+
datastore = find_datastore_by_name_or_id(cloud['id'], datastore_id)
|
150
163
|
return 1 if datastore.nil?
|
151
164
|
json_response = {'datastore' => datastore} # skip redundant request
|
152
165
|
# json_response = @datastores_interface.get(datastore['id'])
|
@@ -200,6 +213,21 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
200
213
|
print as_pretty_table(rows, columns)
|
201
214
|
end
|
202
215
|
|
216
|
+
if datastore['tenants'].nil? || datastore['tenants'].nil?
|
217
|
+
#print "\n", "No tenant permissions found", "\n"
|
218
|
+
else
|
219
|
+
print_h2 "Tenant Permissions"
|
220
|
+
rows = []
|
221
|
+
rows = datastore['tenants'] || []
|
222
|
+
tenant_columns = {
|
223
|
+
"TENANT" => 'name',
|
224
|
+
#"DEFAULT" => lambda {|it| format_boolean(it['defaultTarget']) },
|
225
|
+
"IMAGE TARGET" => lambda {|it| format_boolean(it['defaultStore']) }
|
226
|
+
}
|
227
|
+
print cyan
|
228
|
+
print as_pretty_table(rows, tenant_columns)
|
229
|
+
end
|
230
|
+
|
203
231
|
print reset,"\n"
|
204
232
|
return 0
|
205
233
|
rescue RestClient::Exception => e
|
@@ -210,16 +238,18 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
210
238
|
|
211
239
|
def update(args)
|
212
240
|
options = {}
|
241
|
+
datastore_id = nil
|
213
242
|
cloud_id = nil
|
214
243
|
tenants = nil
|
215
244
|
group_access_all = nil
|
216
245
|
group_access_list = nil
|
217
246
|
group_defaults_list = nil
|
218
247
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
219
|
-
opts.banner = subcommand_usage("[
|
248
|
+
opts.banner = subcommand_usage("[cloud] [datastore] [options]")
|
220
249
|
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
221
250
|
cloud_id = val
|
222
251
|
end
|
252
|
+
opts.add_hidden_option('-c') # prefer args[0] for [cloud]
|
223
253
|
opts.on('--group-access-all [on|off]', String, "Toggle Access for all groups.") do |val|
|
224
254
|
group_access_all = val.to_s == 'on' || val.to_s == 'true'
|
225
255
|
end
|
@@ -252,13 +282,17 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
252
282
|
end
|
253
283
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
254
284
|
opts.footer = "Update a datastore." + "\n" +
|
255
|
-
"[
|
285
|
+
"[cloud] is required. This is the name or id of the cloud." + "\n" +
|
286
|
+
"[datastore] is required. This is the name or id of a datastore."
|
256
287
|
end
|
257
288
|
optparse.parse!(args)
|
258
|
-
if args.count
|
259
|
-
|
260
|
-
|
261
|
-
|
289
|
+
if args.count == 2
|
290
|
+
cloud_id = args[0]
|
291
|
+
datastore_id = args[1]
|
292
|
+
elsif args.count == 1 && cloud_id
|
293
|
+
datastore_id = args[0]
|
294
|
+
else
|
295
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
262
296
|
end
|
263
297
|
connect(options)
|
264
298
|
|
@@ -271,7 +305,7 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
271
305
|
cloud = find_cloud_by_name_or_id(cloud_id)
|
272
306
|
return 1 if cloud.nil?
|
273
307
|
|
274
|
-
datastore = find_datastore_by_name_or_id(cloud['id'],
|
308
|
+
datastore = find_datastore_by_name_or_id(cloud['id'], datastore_id)
|
275
309
|
return 1 if datastore.nil?
|
276
310
|
|
277
311
|
# merge -O options into normally parsed options
|
@@ -324,6 +358,10 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
324
358
|
payload['datastore']['visibility'] = options['visibility']
|
325
359
|
end
|
326
360
|
|
361
|
+
if payload['datastore'].empty? && payload['resourcePermissions'].nil? && payload['tenantPermissions'].nil?
|
362
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
363
|
+
end
|
364
|
+
|
327
365
|
end
|
328
366
|
@cloud_datastores_interface.setopts(options)
|
329
367
|
if options[:dry_run]
|
@@ -0,0 +1,463 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'optparse'
|
3
|
+
require 'filesize'
|
4
|
+
require 'morpheus/cli/cli_command'
|
5
|
+
require 'morpheus/cli/mixins/infrastructure_helper'
|
6
|
+
|
7
|
+
class Morpheus::Cli::CloudFoldersCommand
|
8
|
+
include Morpheus::Cli::CliCommand
|
9
|
+
include Morpheus::Cli::InfrastructureHelper
|
10
|
+
|
11
|
+
#set_command_name :'cloud-folders'
|
12
|
+
set_command_name :'resource-folders'
|
13
|
+
|
14
|
+
register_subcommands :list, :get, :update
|
15
|
+
|
16
|
+
def initialize()
|
17
|
+
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
18
|
+
end
|
19
|
+
|
20
|
+
def connect(opts)
|
21
|
+
@api_client = establish_remote_appliance_connection(opts)
|
22
|
+
@cloud_folders_interface = @api_client.cloud_folders
|
23
|
+
@clouds_interface = @api_client.clouds
|
24
|
+
@options_interface = @api_client.options
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle(args)
|
28
|
+
handle_subcommand(args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def list(args)
|
32
|
+
cloud_id = nil
|
33
|
+
options = {}
|
34
|
+
params = {}
|
35
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
36
|
+
opts.banner = subcommand_usage("[cloud]")
|
37
|
+
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
38
|
+
cloud_id = val
|
39
|
+
end
|
40
|
+
opts.add_hidden_option('-c') # prefer args[0] for [cloud]
|
41
|
+
build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
42
|
+
opts.footer = "List resource folders for a cloud." + "\n" +
|
43
|
+
"[cloud] is required. This is the name or id of the cloud."
|
44
|
+
end
|
45
|
+
optparse.parse!(args)
|
46
|
+
if args.count == 1
|
47
|
+
cloud_id = args[0]
|
48
|
+
elsif args.count == 0 && cloud_id
|
49
|
+
# support -c
|
50
|
+
else
|
51
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
52
|
+
end
|
53
|
+
connect(options)
|
54
|
+
begin
|
55
|
+
# load cloud
|
56
|
+
if cloud_id.nil?
|
57
|
+
puts_error "#{Morpheus::Terminal.angry_prompt}missing required option: [cloud]\n#{optparse}"
|
58
|
+
return 1
|
59
|
+
end
|
60
|
+
cloud = find_cloud_by_name_or_id(cloud_id)
|
61
|
+
return 1 if cloud.nil?
|
62
|
+
|
63
|
+
params.merge!(parse_list_options(options))
|
64
|
+
@cloud_folders_interface.setopts(options)
|
65
|
+
if options[:dry_run]
|
66
|
+
print_dry_run @cloud_folders_interface.dry.list(cloud['id'], params)
|
67
|
+
return
|
68
|
+
end
|
69
|
+
json_response = @cloud_folders_interface.list(cloud['id'], params)
|
70
|
+
folders = json_response["folders"]
|
71
|
+
if options[:json]
|
72
|
+
puts as_json(json_response, options, "folders")
|
73
|
+
return 0
|
74
|
+
elsif options[:yaml]
|
75
|
+
puts as_yaml(json_response, options, "folders")
|
76
|
+
return 0
|
77
|
+
elsif options[:csv]
|
78
|
+
puts records_as_csv(folders, options)
|
79
|
+
return 0
|
80
|
+
end
|
81
|
+
title = "Morpheus Resource Folders - Cloud: #{cloud['name']}"
|
82
|
+
subtitles = []
|
83
|
+
subtitles += parse_list_subtitles(options)
|
84
|
+
print_h1 title, subtitles
|
85
|
+
if folders.empty?
|
86
|
+
print cyan,"No resource folders found.",reset,"\n"
|
87
|
+
else
|
88
|
+
rows = folders.collect {|folder|
|
89
|
+
formatted_name = (folder['depth'] && folder['depth'] > 0) ? ((' ' * folder['depth'].to_i) + folder['name'].to_s) : folder['name'].to_s
|
90
|
+
row = {
|
91
|
+
id: folder['id'],
|
92
|
+
# name: folder['name'],
|
93
|
+
name: formatted_name,
|
94
|
+
type: folder['type'].to_s.capitalize,
|
95
|
+
description: folder['description'],
|
96
|
+
active: format_boolean(folder['active']),
|
97
|
+
visibility: folder['visibility'].to_s.capitalize,
|
98
|
+
default: format_boolean(folder['defaultFolder']),
|
99
|
+
imageTarget: format_boolean(folder['defaultStore']),
|
100
|
+
tenants: folder['tenants'] ? folder['tenants'].collect {|it| it['name'] }.uniq.join(', ') : ''
|
101
|
+
# owner: folder['owner'] ? folder['owner']['name'] : ''
|
102
|
+
}
|
103
|
+
row
|
104
|
+
}
|
105
|
+
columns = [:id, :name, :active, :default, :visibility, :tenants]
|
106
|
+
if options[:include_fields]
|
107
|
+
columns = options[:include_fields]
|
108
|
+
end
|
109
|
+
print cyan
|
110
|
+
print as_pretty_table(rows, columns, options)
|
111
|
+
print reset
|
112
|
+
print_results_pagination(json_response)
|
113
|
+
end
|
114
|
+
print reset,"\n"
|
115
|
+
return 0
|
116
|
+
rescue RestClient::Exception => e
|
117
|
+
print_rest_exception(e, options)
|
118
|
+
exit 1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def get(args)
|
123
|
+
folder_id = nil
|
124
|
+
cloud_id = nil
|
125
|
+
options = {}
|
126
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
127
|
+
opts.banner = subcommand_usage("[cloud] [folder]")
|
128
|
+
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
129
|
+
cloud_id = val
|
130
|
+
end
|
131
|
+
opts.add_hidden_option('-c') # prefer args[0] for [cloud]
|
132
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
133
|
+
opts.footer = "Get details about a resource folder." + "\n" +
|
134
|
+
"[cloud] is required. This is the name or id of the cloud." + "\n"
|
135
|
+
"[folder] is required. This is the name or id of a resource folder."
|
136
|
+
end
|
137
|
+
optparse.parse!(args)
|
138
|
+
if args.count == 2
|
139
|
+
cloud_id = args[0]
|
140
|
+
folder_id = args[1]
|
141
|
+
elsif args.count == 1 && cloud_id
|
142
|
+
folder_id = args[0]
|
143
|
+
else
|
144
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
145
|
+
end
|
146
|
+
connect(options)
|
147
|
+
begin
|
148
|
+
# load cloud
|
149
|
+
if cloud_id.nil?
|
150
|
+
puts_error "#{Morpheus::Terminal.angry_prompt}missing required option: [cloud]\n#{optparse}"
|
151
|
+
return 1
|
152
|
+
end
|
153
|
+
cloud = find_cloud_by_name_or_id(cloud_id)
|
154
|
+
return 1 if cloud.nil?
|
155
|
+
@cloud_folders_interface.setopts(options)
|
156
|
+
if options[:dry_run]
|
157
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
158
|
+
print_dry_run @cloud_folders_interface.dry.get(cloud['id'], folder_id.to_i)
|
159
|
+
else
|
160
|
+
print_dry_run @cloud_folders_interface.dry.list(cloud['id'], {name:folder_id})
|
161
|
+
end
|
162
|
+
return
|
163
|
+
end
|
164
|
+
folder = find_folder_by_name_or_id(cloud['id'], folder_id)
|
165
|
+
return 1 if folder.nil?
|
166
|
+
json_response = {'folder' => folder} # skip redundant request
|
167
|
+
# json_response = @folders_interface.get(folder['id'])
|
168
|
+
folder = json_response['folder']
|
169
|
+
if options[:json]
|
170
|
+
puts as_json(json_response, options, "folder")
|
171
|
+
return 0
|
172
|
+
elsif options[:yaml]
|
173
|
+
puts as_yaml(json_response, options, "folder")
|
174
|
+
return 0
|
175
|
+
elsif options[:csv]
|
176
|
+
puts records_as_csv([folder], options)
|
177
|
+
return 0
|
178
|
+
end
|
179
|
+
print_h1 "Resource Folder Details"
|
180
|
+
print cyan
|
181
|
+
description_cols = {
|
182
|
+
"ID" => 'id',
|
183
|
+
"Name" => 'name',
|
184
|
+
"Description" => 'description',
|
185
|
+
#"Type" => lambda {|it| it['type'].to_s.capitalize },
|
186
|
+
"Cloud" => lambda {|it| it['zone'] ? it['zone']['name'] : '' },
|
187
|
+
"Active" => lambda {|it| format_boolean(it['active']) },
|
188
|
+
"Default" => lambda {|it| format_boolean(it['defaultPool']) },
|
189
|
+
"Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
|
190
|
+
#"Tenants" => lambda {|it| it['tenants'] ? it['tenants'].collect {|it| it['name'] }.uniq.join(', ') : '' }
|
191
|
+
# "Owner" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
|
192
|
+
}
|
193
|
+
print_description_list(description_cols, folder)
|
194
|
+
|
195
|
+
if folder['resourcePermission'].nil?
|
196
|
+
#print "\n", "No group access found", "\n"
|
197
|
+
else
|
198
|
+
print_h2 "Group Access"
|
199
|
+
rows = []
|
200
|
+
if folder['resourcePermission']['all']
|
201
|
+
rows.push({"name" => 'All'})
|
202
|
+
end
|
203
|
+
if folder['resourcePermission']['sites']
|
204
|
+
folder['resourcePermission']['sites'].each do |site|
|
205
|
+
rows.push(site)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
group_columns = {
|
209
|
+
"GROUP" => 'name',
|
210
|
+
"DEFAULT" => lambda {|it| it['default'].nil? ? '' : format_boolean(it['default']) }
|
211
|
+
}
|
212
|
+
print cyan
|
213
|
+
print as_pretty_table(rows, group_columns)
|
214
|
+
end
|
215
|
+
|
216
|
+
if folder['resourcePermission'] && folder['resourcePermission']['plans'] && folder['resourcePermission']['plans'].size > 0
|
217
|
+
print_h2 "Service Plan Access"
|
218
|
+
rows = []
|
219
|
+
if folder['resourcePermission']['allPlans']
|
220
|
+
rows.push({"name" => 'All'})
|
221
|
+
end
|
222
|
+
if folder['resourcePermission']['plans']
|
223
|
+
folder['resourcePermission']['plans'].each do |plan|
|
224
|
+
rows.push(plan)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
# rows = rows.collect do |site|
|
228
|
+
# {plan: site['name'], default: site['default'] ? 'Yes' : ''}
|
229
|
+
# #{group: site['name']}
|
230
|
+
# end
|
231
|
+
plan_columns = {
|
232
|
+
"PLAN" => 'name',
|
233
|
+
"DEFAULT" => lambda {|it| it['default'].nil? ? '' : format_boolean(it['default']) }
|
234
|
+
}
|
235
|
+
print cyan
|
236
|
+
print as_pretty_table(rows, plan_columns)
|
237
|
+
end
|
238
|
+
|
239
|
+
if folder['tenants'].nil? || folder['tenants'].empty?
|
240
|
+
#print "\n", "No tenant permissions found", "\n"
|
241
|
+
else
|
242
|
+
print_h2 "Tenant Permissions"
|
243
|
+
rows = []
|
244
|
+
rows = folder['tenants'] || []
|
245
|
+
tenant_columns = {
|
246
|
+
"TENANT" => 'name',
|
247
|
+
"DEFAULT" => lambda {|it| format_boolean(it['defaultTarget']) },
|
248
|
+
"IMAGE TARGET" => lambda {|it| format_boolean(it['defaultStore']) }
|
249
|
+
}
|
250
|
+
print cyan
|
251
|
+
print as_pretty_table(rows, tenant_columns)
|
252
|
+
end
|
253
|
+
|
254
|
+
print reset,"\n"
|
255
|
+
return 0
|
256
|
+
rescue RestClient::Exception => e
|
257
|
+
print_rest_exception(e, options)
|
258
|
+
return 1
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def update(args)
|
263
|
+
options = {}
|
264
|
+
cloud_id = nil
|
265
|
+
folder_id = nil
|
266
|
+
tenants = nil
|
267
|
+
group_access_all = nil
|
268
|
+
group_access_list = nil
|
269
|
+
group_defaults_list = nil
|
270
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
271
|
+
opts.banner = subcommand_usage("[cloud] [folder] [options]")
|
272
|
+
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
273
|
+
cloud_id = val
|
274
|
+
end
|
275
|
+
opts.add_hidden_option('-c') # prefer args[0] for [cloud]
|
276
|
+
opts.on('--group-access-all [on|off]', String, "Toggle Access for all groups.") do |val|
|
277
|
+
group_access_all = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
278
|
+
end
|
279
|
+
opts.on('--group-access LIST', Array, "Group Access, comma separated list of group IDs.") do |list|
|
280
|
+
if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
281
|
+
group_access_list = []
|
282
|
+
else
|
283
|
+
group_access_list = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
284
|
+
end
|
285
|
+
end
|
286
|
+
# opts.on('--group-defaults LIST', Array, "Group Default Selection, comma separated list of group IDs") do |list|
|
287
|
+
# if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
288
|
+
# group_defaults_list = []
|
289
|
+
# else
|
290
|
+
# group_defaults_list = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
291
|
+
# end
|
292
|
+
# end
|
293
|
+
opts.on('--tenants LIST', Array, "Tenant Access, comma separated list of account IDs") do |list|
|
294
|
+
if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
295
|
+
options['tenants'] = []
|
296
|
+
else
|
297
|
+
options['tenants'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
298
|
+
end
|
299
|
+
end
|
300
|
+
opts.on('--visibility [private|public]', String, "Visibility") do |val|
|
301
|
+
options['visibility'] = val
|
302
|
+
end
|
303
|
+
opts.on('--active [on|off]', String, "Can be used to disable a resource folder") do |val|
|
304
|
+
options['active'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
305
|
+
end
|
306
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
307
|
+
opts.footer = "Update a resource folder." + "\n" +
|
308
|
+
"[cloud] is required. This is the name or id of the cloud." + "\n"
|
309
|
+
"[folder] is required. This is the id of a folder."
|
310
|
+
end
|
311
|
+
optparse.parse!(args)
|
312
|
+
if args.count == 2
|
313
|
+
cloud_id = args[0]
|
314
|
+
folder_id = args[1]
|
315
|
+
elsif args.count == 1 && cloud_id
|
316
|
+
folder_id = args[0]
|
317
|
+
else
|
318
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
319
|
+
end
|
320
|
+
|
321
|
+
connect(options)
|
322
|
+
|
323
|
+
begin
|
324
|
+
# load cloud
|
325
|
+
if cloud_id.nil?
|
326
|
+
puts_error "#{Morpheus::Terminal.angry_prompt}missing required option: [cloud]\n#{optparse}"
|
327
|
+
return 1
|
328
|
+
end
|
329
|
+
cloud = find_cloud_by_name_or_id(cloud_id)
|
330
|
+
return 1 if cloud.nil?
|
331
|
+
|
332
|
+
folder = find_folder_by_name_or_id(cloud['id'], folder_id)
|
333
|
+
return 1 if folder.nil?
|
334
|
+
|
335
|
+
# merge -O options into normally parsed options
|
336
|
+
options.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
337
|
+
|
338
|
+
# construct payload
|
339
|
+
payload = nil
|
340
|
+
if options[:payload]
|
341
|
+
payload = options[:payload]
|
342
|
+
else
|
343
|
+
# prompt for resource folder options
|
344
|
+
payload = {
|
345
|
+
'folder' => {
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
349
|
+
# allow arbitrary -O options
|
350
|
+
payload['folder'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
351
|
+
|
352
|
+
|
353
|
+
# Group Access
|
354
|
+
if group_access_all != nil
|
355
|
+
payload['resourcePermissions'] ||= {}
|
356
|
+
payload['resourcePermissions']['all'] = group_access_all
|
357
|
+
end
|
358
|
+
if group_access_list != nil
|
359
|
+
payload['resourcePermissions'] ||= {}
|
360
|
+
payload['resourcePermissions']['sites'] = group_access_list.collect do |site_id|
|
361
|
+
site = {"id" => site_id.to_i}
|
362
|
+
if group_defaults_list && group_defaults_list.include?(site_id)
|
363
|
+
site["default"] = true
|
364
|
+
end
|
365
|
+
site
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# Tenants
|
370
|
+
if options['tenants']
|
371
|
+
payload['tenantPermissions'] = {}
|
372
|
+
payload['tenantPermissions']['accounts'] = options['tenants']
|
373
|
+
end
|
374
|
+
|
375
|
+
# Active
|
376
|
+
if options['active'] != nil
|
377
|
+
payload['folder']['active'] = options['active']
|
378
|
+
end
|
379
|
+
|
380
|
+
# Visibility
|
381
|
+
if options['visibility'] != nil
|
382
|
+
payload['folder']['visibility'] = options['visibility']
|
383
|
+
end
|
384
|
+
|
385
|
+
if payload['folder'].empty? && payload['resourcePermissions'].nil? && payload['tenantPermissions'].nil?
|
386
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
@cloud_folders_interface.setopts(options)
|
391
|
+
if options[:dry_run]
|
392
|
+
print_dry_run @cloud_folders_interface.dry.update(cloud['id'], folder["id"], payload)
|
393
|
+
return
|
394
|
+
end
|
395
|
+
json_response = @cloud_folders_interface.update(cloud['id'], folder["id"], payload)
|
396
|
+
if options[:json]
|
397
|
+
puts as_json(json_response)
|
398
|
+
else
|
399
|
+
folder = json_response['folder']
|
400
|
+
print_green_success "Updated resource folder #{folder['name']}"
|
401
|
+
get([folder['id'], "-c", cloud['id'].to_s]) # argh, to_s needed on option values..
|
402
|
+
end
|
403
|
+
return 0
|
404
|
+
rescue RestClient::Exception => e
|
405
|
+
print_rest_exception(e, options)
|
406
|
+
return 1
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
private
|
411
|
+
|
412
|
+
|
413
|
+
def find_folder_by_name_or_id(cloud_id, val)
|
414
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
415
|
+
return find_folder_by_id(cloud_id, val)
|
416
|
+
else
|
417
|
+
return find_folder_by_name(cloud_id, val)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
def find_folder_by_id(cloud_id, id)
|
422
|
+
begin
|
423
|
+
json_response = @cloud_folders_interface.get(cloud_id, id.to_i)
|
424
|
+
return json_response['folder']
|
425
|
+
rescue RestClient::Exception => e
|
426
|
+
if e.response && e.response.code == 404
|
427
|
+
print_red_alert "Resource Folder not found by id #{id}"
|
428
|
+
return nil
|
429
|
+
else
|
430
|
+
raise e
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
def find_folder_by_name(cloud_id, name)
|
436
|
+
json_response = @cloud_folders_interface.list(cloud_id, {name: name.to_s})
|
437
|
+
folders = json_response['folders']
|
438
|
+
if folders.empty?
|
439
|
+
print_red_alert "Resource Folder not found by name #{name}"
|
440
|
+
return nil
|
441
|
+
elsif folders.size > 1
|
442
|
+
matching_folders = folders.select { |it| it['name'] == name }
|
443
|
+
if matching_folders.size == 1
|
444
|
+
return matching_folders[0]
|
445
|
+
end
|
446
|
+
print_red_alert "#{folders.size} resource folders found by name #{name}"
|
447
|
+
rows = folders.collect do |it|
|
448
|
+
{id: it['id'], name: it['name']}
|
449
|
+
end
|
450
|
+
print "\n"
|
451
|
+
puts as_pretty_table(rows, [:id, :name], {color:red})
|
452
|
+
return nil
|
453
|
+
else
|
454
|
+
folder = folders[0]
|
455
|
+
# merge in tenants map
|
456
|
+
if json_response['tenants'] && json_response['tenants'][folder['id']]
|
457
|
+
folder['tenants'] = json_response['tenants'][folder['id']]
|
458
|
+
end
|
459
|
+
return folder
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
end
|