morpheus-cli 5.3.2 → 5.3.3
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/Dockerfile +1 -1
- data/lib/morpheus/api/accounts_interface.rb +4 -30
- data/lib/morpheus/api/api_client.rb +12 -0
- data/lib/morpheus/api/instances_interface.rb +18 -5
- data/lib/morpheus/api/load_balancer_pools_interface.rb +9 -0
- data/lib/morpheus/api/load_balancer_types_interface.rb +9 -0
- data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +9 -0
- data/lib/morpheus/api/load_balancers_interface.rb +4 -53
- data/lib/morpheus/api/network_routers_interface.rb +56 -0
- data/lib/morpheus/api/secondary_read_interface.rb +25 -0
- data/lib/morpheus/api/secondary_rest_interface.rb +42 -0
- data/lib/morpheus/api/virtual_images_interface.rb +23 -2
- data/lib/morpheus/cli/apps.rb +3 -2
- data/lib/morpheus/cli/cli_command.rb +21 -14
- data/lib/morpheus/cli/cli_registry.rb +55 -2
- data/lib/morpheus/cli/cloud_resource_pools_command.rb +169 -133
- data/lib/morpheus/cli/clusters.rb +51 -33
- data/lib/morpheus/cli/instances.rb +292 -174
- data/lib/morpheus/cli/invoices_command.rb +79 -99
- data/lib/morpheus/cli/library_cluster_layouts_command.rb +20 -0
- data/lib/morpheus/cli/load_balancer_types.rb +37 -0
- data/lib/morpheus/cli/load_balancers.rb +149 -314
- data/lib/morpheus/cli/log_settings_command.rb +7 -3
- data/lib/morpheus/cli/mixins/load_balancers_helper.rb +156 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +11 -0
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +123 -101
- data/lib/morpheus/cli/mixins/rest_command.rb +657 -0
- data/lib/morpheus/cli/monitoring_checks_command.rb +2 -0
- data/lib/morpheus/cli/network_routers_command.rb +1183 -185
- data/lib/morpheus/cli/networks_command.rb +194 -101
- data/lib/morpheus/cli/option_parser.rb +25 -17
- data/lib/morpheus/cli/option_types.rb +42 -45
- data/lib/morpheus/cli/tenants_command.rb +18 -20
- data/lib/morpheus/cli/vdi_pools_command.rb +4 -1
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +249 -29
- data/lib/morpheus/cli.rb +1 -0
- data/lib/morpheus/ext/string.rb +41 -0
- data/lib/morpheus/formatters.rb +4 -0
- data/morpheus-cli.gemspec +1 -1
- metadata +13 -4
@@ -32,27 +32,35 @@ module Morpheus
|
|
32
32
|
#out << "Options:\n"
|
33
33
|
# the default way..
|
34
34
|
# out << summarize().join("")
|
35
|
-
|
36
35
|
# super hacky, should be examining the option, not the fully formatted description
|
37
36
|
my_summaries = summarize()
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
37
|
+
if opts[:show_hidden_options]
|
38
|
+
my_summaries.each do |full_line|
|
39
|
+
out << full_line
|
40
|
+
end
|
41
|
+
else
|
42
|
+
on_hidden_option = false
|
43
|
+
my_summaries.each do |full_line|
|
44
|
+
opt_description = full_line.to_s.strip
|
45
|
+
if opt_description.start_with?("-")
|
46
|
+
is_hidden = (@hidden_options || []).find { |hidden_switch|
|
47
|
+
if hidden_switch.start_with?("-")
|
48
|
+
opt_description.start_with?("#{hidden_switch} ")
|
49
|
+
else
|
50
|
+
opt_description.start_with?("--#{hidden_switch} ")
|
51
|
+
end
|
52
|
+
}
|
53
|
+
if is_hidden
|
54
|
+
on_hidden_option = true
|
55
|
+
else
|
56
|
+
on_hidden_option = false
|
57
|
+
out << full_line
|
58
|
+
end
|
51
59
|
else
|
52
|
-
|
60
|
+
if on_hidden_option == false
|
61
|
+
out << full_line
|
62
|
+
end
|
53
63
|
end
|
54
|
-
else
|
55
|
-
out << opt_description
|
56
64
|
end
|
57
65
|
end
|
58
66
|
end
|
@@ -61,11 +61,11 @@ module Morpheus
|
|
61
61
|
).each do |option_type|
|
62
62
|
context_map = results
|
63
63
|
value = nil
|
64
|
-
value_found=false
|
64
|
+
value_found = false
|
65
|
+
field_group = (option_type['fieldGroup'] || 'default').to_s.sub(/options\Z/i, "").strip # avoid "ADVANCED OPTION OPTIONS"
|
65
66
|
|
66
|
-
if cur_field_group !=
|
67
|
-
cur_field_group =
|
68
|
-
cur_field_group = cur_field_group.to_s.sub(/options\Z/i, "").strip # avoid "ADVANCED OPTION OPTIONS"
|
67
|
+
if cur_field_group != field_group
|
68
|
+
cur_field_group = field_group
|
69
69
|
print "\n#{cur_field_group.upcase} OPTIONS\n#{"=" * ("#{cur_field_group} OPTIONS".length)}\n\n"
|
70
70
|
end
|
71
71
|
|
@@ -94,46 +94,42 @@ module Morpheus
|
|
94
94
|
end
|
95
95
|
|
96
96
|
if !visible_option_check_value.to_s.empty?
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
depends_on_values = []
|
103
|
-
if depends_on_value.size > 0
|
104
|
-
# strip parenthesis
|
105
|
-
if depends_on_value[0] && depends_on_value[0].chr == "("
|
106
|
-
depends_on_value = depends_on_value[1..-1]
|
107
|
-
end
|
108
|
-
depends_on_value.chomp(")")
|
109
|
-
depends_on_values = depends_on_value.split("|").collect { |it| it.strip }
|
110
|
-
end
|
111
|
-
depends_on_option_type = option_types.find {|it| it["code"] == depends_on_code }
|
112
|
-
if !depends_on_option_type
|
113
|
-
depends_on_option_type = option_types.find {|it|
|
114
|
-
(it['fieldContext'] ? "#{it['fieldContext']}.#{it['fieldName']}" : it['fieldName']) == depends_on_code
|
115
|
-
}
|
97
|
+
match_type = 'any'
|
98
|
+
|
99
|
+
if visible_option_check_value.include?('::')
|
100
|
+
match_type = 'all' if visible_option_check_value.start_with?('matchAll')
|
101
|
+
visible_option_check_value = visible_option_check_value[visible_option_check_value.index('::') + 2..-1]
|
116
102
|
end
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
103
|
+
|
104
|
+
found_dep_value = match_type == 'all' ? true : false
|
105
|
+
visible_option_check_value.sub(',', ' ').split(' ').each do |value|
|
106
|
+
parts = value.split(':')
|
107
|
+
depends_on_code = parts[0]
|
108
|
+
depends_on_value = parts.count > 1 ? parts[1].to_s.strip : nil
|
109
|
+
depends_on_option_type = option_types.find {|it| it["code"] == depends_on_code }
|
110
|
+
if !depends_on_option_type
|
111
|
+
depends_on_option_type = option_types.find {|it|
|
112
|
+
(it['fieldContext'] ? "#{it['fieldContext']}.#{it['fieldName']}" : it['fieldName']) == depends_on_code
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
if depends_on_option_type
|
117
|
+
depends_on_field_key = depends_on_option_type['fieldContext'].nil? || depends_on_option_type['fieldContext'].empty? ? "#{depends_on_option_type['fieldName']}" : "#{depends_on_option_type['fieldContext']}.#{depends_on_option_type['fieldName']}"
|
128
118
|
else
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
119
|
+
depends_on_field_key = depends_on_code
|
120
|
+
end
|
121
|
+
|
122
|
+
field_value = get_object_value(results, depends_on_field_key) ||
|
123
|
+
get_object_value(options, depends_on_field_key) ||
|
124
|
+
get_object_value(api_params, depends_on_field_key)
|
125
|
+
|
126
|
+
if !field_value.nil? && (depends_on_value.nil? || depends_on_value.empty? || field_value.match?(depends_on_value))
|
127
|
+
found_dep_value = true if match_type != 'all'
|
128
|
+
else
|
129
|
+
found_dep_value = false if match_type == 'all'
|
133
130
|
end
|
134
|
-
else
|
135
|
-
# could not find the dependent option type, proceed and prompt
|
136
131
|
end
|
132
|
+
next if !found_dep_value
|
137
133
|
end
|
138
134
|
|
139
135
|
cur_namespace = options
|
@@ -246,7 +242,7 @@ module Morpheus
|
|
246
242
|
# I suppose the entered value should take precedence
|
247
243
|
# api_params = api_params.merge(options) # this might be good enough
|
248
244
|
# dup it
|
249
|
-
value = select_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).
|
245
|
+
value = select_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).deep_merge(results)), options[:no_prompt], nil, paging_enabled)
|
250
246
|
if value && option_type['type'] == 'multiSelect'
|
251
247
|
value = [value]
|
252
248
|
while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
|
@@ -283,10 +279,10 @@ module Morpheus
|
|
283
279
|
|
284
280
|
if option_type['type'] == 'multiSelect'
|
285
281
|
value = [value] if !value.nil? && !value.is_a?(Array)
|
286
|
-
parent_context_map[parent_ns] = value
|
287
|
-
else
|
288
|
-
context_map[field_name] = value
|
282
|
+
# parent_context_map[parent_ns] = value
|
289
283
|
end
|
284
|
+
context_map[field_name] = value if !(value.nil? || (value.is_a?(Hash) && value.empty?))
|
285
|
+
parent_context_map.reject! {|k,v| k == parent_ns && (v.nil? || (v.is_a?(Hash) && v.empty?))}
|
290
286
|
end
|
291
287
|
results
|
292
288
|
end
|
@@ -957,8 +953,9 @@ module Morpheus
|
|
957
953
|
out << "\n"
|
958
954
|
out << "#{header}\n"
|
959
955
|
out << "#{'=' * header.length}\n"
|
956
|
+
|
960
957
|
select_options.each do |option|
|
961
|
-
out << " * #{option['name']} [#{option['value']}]\n"
|
958
|
+
out << (option['isGroup'] ? "- #{option['name']}\n" : " * #{option['name']} [#{option['value']}]\n")
|
962
959
|
end
|
963
960
|
return out
|
964
961
|
end
|
@@ -248,41 +248,39 @@ EOT
|
|
248
248
|
|
249
249
|
|
250
250
|
def remove(args)
|
251
|
+
params = {}
|
251
252
|
options = {}
|
252
253
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
253
|
-
opts.banner = subcommand_usage("[
|
254
|
-
|
254
|
+
opts.banner = subcommand_usage("[tenant]")
|
255
|
+
opts.on('--remove-resources [on|off]', ['on','off'], "Remove Infrastructure. Default is off.") do |val|
|
256
|
+
params[:removeResources] = val.nil? ? 'on' : val
|
257
|
+
end
|
258
|
+
build_standard_remove_options(opts, options)
|
259
|
+
opts.footer = <<-EOT
|
260
|
+
Delete a tenant.
|
261
|
+
[tenant] is required. This is the name or id of a tenant.
|
262
|
+
EOT
|
255
263
|
end
|
256
264
|
optparse.parse!(args)
|
257
|
-
|
258
|
-
|
259
|
-
exit 1
|
260
|
-
end
|
265
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
266
|
+
optparse.parse!(args)
|
261
267
|
connect(options)
|
262
268
|
begin
|
263
269
|
# allow finding by ID since name is not unique!
|
264
270
|
account = find_account_by_name_or_id(args[0])
|
265
|
-
|
266
|
-
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the
|
267
|
-
|
268
|
-
end
|
269
|
-
if options[:dry_run] && options[:json]
|
270
|
-
puts as_json(payload, options)
|
271
|
-
return 0
|
271
|
+
return 1, "tenant not found" if account.nil?
|
272
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the tenant #{account['name']}?")
|
273
|
+
return 9, "aborted command"
|
272
274
|
end
|
273
275
|
@accounts_interface.setopts(options)
|
274
276
|
if options[:dry_run]
|
275
|
-
print_dry_run @accounts_interface.dry.destroy(account['id'])
|
277
|
+
print_dry_run @accounts_interface.dry.destroy(account['id'], params)
|
276
278
|
return
|
277
279
|
end
|
278
|
-
json_response = @accounts_interface.destroy(account['id'])
|
279
|
-
|
280
|
-
print JSON.pretty_generate(json_response)
|
281
|
-
print "\n"
|
282
|
-
else
|
280
|
+
json_response = @accounts_interface.destroy(account['id'], params)
|
281
|
+
render_response(json_response, options) do
|
283
282
|
print_green_success "Tenant #{account['name']} removed"
|
284
283
|
end
|
285
|
-
|
286
284
|
rescue RestClient::Exception => e
|
287
285
|
print_rest_exception(e, options)
|
288
286
|
exit 1
|
@@ -418,6 +418,7 @@ EOT
|
|
418
418
|
"Name" => 'name',
|
419
419
|
"Description" => 'description',
|
420
420
|
"Persistent" => lambda {|it| format_boolean(it['persistentUser']) },
|
421
|
+
"Recyclable" => lambda {|it| it['recyclable'].nil? ? nil : format_boolean(it['recyclable']) },
|
421
422
|
"Enabled" => lambda {|it| format_boolean(it['enabled']) },
|
422
423
|
"Pool Usage" => lambda {|it|
|
423
424
|
# todo: [== ] 2/8 would be neat generate_usage_bar(...)
|
@@ -447,12 +448,13 @@ EOT
|
|
447
448
|
"Max Size" => lambda {|it| format_number(it['maxPoolSize']) rescue '' },
|
448
449
|
"Lease Timeout" => lambda {|it| format_number(it['allocationTimeoutMinutes']) rescue '' },
|
449
450
|
"Persistent" => lambda {|it| format_boolean(it['persistentUser']) },
|
451
|
+
"Recyclable" => lambda {|it| it['recyclable'].nil? ? nil : format_boolean(it['recyclable']) },
|
452
|
+
"Enabled" => lambda {|it| format_boolean(it['enabled']) },
|
450
453
|
"Allow Copy" => lambda {|it| format_boolean(it['allowCopy']) },
|
451
454
|
"Allow Printer" => lambda {|it| format_boolean(it['allowPrinter']) },
|
452
455
|
"Allow File Share" => lambda {|it| format_boolean(it['allowFileshare']) },
|
453
456
|
"Allow Hypervisor Console" => lambda {|it| format_boolean(it['allowHypervisorConsole']) },
|
454
457
|
"Auto Create User" => lambda {|it| format_boolean(it['autoCreateLocalUserOnReservation']) },
|
455
|
-
"Enabled" => lambda {|it| format_boolean(it['enabled']) },
|
456
458
|
"Logo" => lambda {|it| it['logo'] || it['imagePath'] },
|
457
459
|
#"Config" => lambda {|it| it['config'] },
|
458
460
|
"Group" => lambda {|it| it['group'] ? it['group']['name'] : nil },
|
@@ -484,6 +486,7 @@ EOT
|
|
484
486
|
{'fieldName' => 'maxPoolSize', 'fieldLabel' => 'Max Size', 'type' => 'number', 'required' => true, 'description' => 'Max limit on number of allocations and instances within the pool.'},
|
485
487
|
{'fieldName' => 'allocationTimeoutMinutes', 'fieldLabel' => 'Lease Timeout', 'type' => 'number', 'description' => 'Time (in minutes) after a user disconnects before an allocation is recycled or shutdown depending on persistence.'},
|
486
488
|
{'fieldName' => 'persistentUser', 'fieldLabel' => 'Persistent', 'type' => 'checkbox', 'defaultValue' => false},
|
489
|
+
{'fieldName' => 'recyclable', 'fieldLabel' => 'Recyclable', 'type' => 'checkbox', 'defaultValue' => false, 'description' => 'Recyclable VDI Pools only work with cloud types that support snapshot management (i.e. Vmware, Nutanix, VCD)'},
|
487
490
|
{'fieldName' => 'allowCopy', 'fieldLabel' => 'Allow Copy', 'type' => 'checkbox', 'defaultValue' => false},
|
488
491
|
{'fieldName' => 'allowPrinter', 'fieldLabel' => 'Allow Printer', 'type' => 'checkbox', 'defaultValue' => false},
|
489
492
|
{'fieldName' => 'allowFileshare', 'fieldLabel' => 'Allow File Share', 'type' => 'checkbox', 'defaultValue' => false},
|
data/lib/morpheus/cli/version.rb
CHANGED
@@ -11,8 +11,7 @@ class Morpheus::Cli::VirtualImages
|
|
11
11
|
include Morpheus::Cli::ProvisioningHelper
|
12
12
|
|
13
13
|
register_subcommands :list, :get, :add, :add_file, :remove_file, :update, :remove, :types => :virtual_image_types
|
14
|
-
|
15
|
-
set_default_subcommand :list
|
14
|
+
register_subcommands :list_locations, :get_location, :remove_location
|
16
15
|
|
17
16
|
# def initialize()
|
18
17
|
# # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
@@ -188,6 +187,7 @@ EOT
|
|
188
187
|
image = json_response['virtualImage']
|
189
188
|
image_config = image['config'] || {}
|
190
189
|
image_volumes = image['volumes'] || []
|
190
|
+
image_locations = image['locations'] || []
|
191
191
|
image_files = json_response['cloudFiles'] || json_response['files']
|
192
192
|
image_type = virtual_image_type_for_name_or_code(image['imageType'])
|
193
193
|
image_type_display = image_type ? "#{image_type['name']}" : image['imageType']
|
@@ -255,7 +255,7 @@ EOT
|
|
255
255
|
# print "\n", reset
|
256
256
|
end
|
257
257
|
|
258
|
-
if image_files
|
258
|
+
if image_files && !image_files.empty?
|
259
259
|
print_h2 "Files (#{image_files.size})"
|
260
260
|
# image_files.each {|image_file|
|
261
261
|
# pretty_filesize = Filesize.from("#{image_file['size']} B").pretty
|
@@ -270,6 +270,11 @@ EOT
|
|
270
270
|
print as_pretty_table(image_file_rows, [:filename, :size])
|
271
271
|
# print reset,"\n"
|
272
272
|
end
|
273
|
+
|
274
|
+
if image_locations && !image_locations.empty?
|
275
|
+
print_h2 "Locations", options
|
276
|
+
print as_pretty_table(image_locations, virtual_image_location_list_column_definitions.upcase_keys!, options)
|
277
|
+
end
|
273
278
|
|
274
279
|
if options[:details] && image_config && !image_config.empty?
|
275
280
|
print_h2 "Config", options
|
@@ -689,7 +694,7 @@ EOT
|
|
689
694
|
image = find_virtual_image_by_name_or_id(image_name)
|
690
695
|
return 1 if image.nil?
|
691
696
|
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the virtual image filename #{filename}?")
|
692
|
-
|
697
|
+
return 9, "aborted"
|
693
698
|
end
|
694
699
|
@virtual_images_interface.setopts(options)
|
695
700
|
if options[:dry_run]
|
@@ -709,44 +714,188 @@ EOT
|
|
709
714
|
end
|
710
715
|
|
711
716
|
def remove(args)
|
717
|
+
params = {}
|
712
718
|
options = {}
|
713
719
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
714
|
-
opts.banner = subcommand_usage("[
|
715
|
-
|
720
|
+
opts.banner = subcommand_usage("[image] [location]")
|
721
|
+
opts.on('--remove-from-cloud [true|false]', String, "Remove from all clouds. Default is true.") do |val|
|
722
|
+
options[:options]['removeFromCloud'] = ['','true','on'].include?(val.to_s)
|
723
|
+
end
|
724
|
+
build_standard_remove_options(opts, options)
|
725
|
+
opts.footer = <<-EOT
|
726
|
+
Delete a virtual image.
|
727
|
+
[image] is required. This is the name or id of a virtual image.
|
728
|
+
EOT
|
716
729
|
end
|
717
730
|
optparse.parse!(args)
|
718
|
-
|
719
|
-
|
720
|
-
|
731
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
732
|
+
connect(options)
|
733
|
+
image = find_virtual_image_by_name_or_id(args[0])
|
734
|
+
return 1, "virtual image not found for '#{args[0]}'" if image.nil?
|
735
|
+
params.merge!(parse_query_options(options))
|
736
|
+
# Delete prompt
|
737
|
+
# [ X ] Remove from all clouds
|
738
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'removeFromCloud', 'fieldLabel' => 'Remove from all clouds', 'type' => 'checkbox', 'defaultValue' => true, 'required' => true, 'description' => "Remove from all clouds"}], options[:options], @api_client)
|
739
|
+
remove_from_cloud = v_prompt['removeFromCloud'].to_s == 'true' || v_prompt['removeFromCloud'].to_s == 'on'
|
740
|
+
params['removeFromCloud'] = remove_from_cloud
|
741
|
+
|
742
|
+
# Delete confirmation
|
743
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the virtual image #{image['name']}?")
|
744
|
+
return 9, "aborted"
|
745
|
+
end
|
746
|
+
|
747
|
+
@virtual_images_interface.setopts(options)
|
748
|
+
if options[:dry_run]
|
749
|
+
print_dry_run @virtual_images_interface.dry.destroy(image['id'], params)
|
750
|
+
return
|
751
|
+
end
|
752
|
+
json_response = @virtual_images_interface.destroy(image['id'], params)
|
753
|
+
render_response(json_response, options) do
|
754
|
+
print_green_success "Removed virtual image #{image['name']}"
|
755
|
+
end
|
756
|
+
return 0, nil
|
757
|
+
end
|
758
|
+
|
759
|
+
def list_locations(args)
|
760
|
+
params = {}
|
761
|
+
options = {}
|
762
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
763
|
+
opts.banner = subcommand_usage("[image]")
|
764
|
+
build_standard_list_options(opts, options)
|
765
|
+
opts.footer = <<-EOT
|
766
|
+
List virtual image locations for a specific virtual image.
|
767
|
+
[image] is required. This is the name or id of a virtual image.
|
768
|
+
EOT
|
769
|
+
end
|
770
|
+
optparse.parse!(args)
|
771
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
772
|
+
if args.count > 1
|
773
|
+
options[:phrase] = args[1..-1].join(" ")
|
721
774
|
end
|
722
|
-
image_name = args[0]
|
723
775
|
connect(options)
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
776
|
+
image = find_virtual_image_by_name_or_id(args[0])
|
777
|
+
return 1, "virtual image not found for '#{args[0]}'" if image.nil?
|
778
|
+
params.merge!(parse_list_options(options))
|
779
|
+
@virtual_images_interface.setopts(options)
|
780
|
+
if options[:dry_run]
|
781
|
+
print_dry_run @virtual_images_interface.dry.list_locations(image['id'], params)
|
782
|
+
return
|
783
|
+
end
|
784
|
+
json_response = @virtual_images_interface.list_locations(image['id'], params)
|
785
|
+
records = json_response['locations']
|
786
|
+
render_response(json_response, options, 'virtualImages') do
|
787
|
+
title = "Virtual Image Locations"
|
788
|
+
subtitles = parse_list_subtitles(options)
|
789
|
+
print_h1 title, subtitles
|
790
|
+
if records.empty?
|
791
|
+
print cyan,"No virtual image locations found.",reset,"\n"
|
738
792
|
else
|
739
|
-
print
|
793
|
+
print as_pretty_table(records, virtual_image_location_list_column_definitions.upcase_keys!, options)
|
794
|
+
print_results_pagination(json_response)
|
740
795
|
end
|
741
|
-
|
742
|
-
|
743
|
-
|
796
|
+
print reset,"\n"
|
797
|
+
end
|
798
|
+
return 0, nil
|
799
|
+
end
|
800
|
+
|
801
|
+
def get_location(args)
|
802
|
+
params = {}
|
803
|
+
options = {}
|
804
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
805
|
+
opts.banner = subcommand_usage("[image] [location]")
|
806
|
+
build_standard_remove_options(opts, options)
|
807
|
+
opts.footer = <<-EOT
|
808
|
+
Get details about a virtual image location.
|
809
|
+
[image] is required. This is the name or id of a virtual image.
|
810
|
+
[location] is required. This is the name or id of a virtual image location.
|
811
|
+
EOT
|
744
812
|
end
|
813
|
+
optparse.parse!(args)
|
814
|
+
verify_args!(args:args, optparse:optparse, count:2)
|
815
|
+
connect(options)
|
816
|
+
image = find_virtual_image_by_name_or_id(args[0])
|
817
|
+
return 1, "virtual image not found for '#{args[0]}'" if image.nil?
|
818
|
+
location = find_virtual_image_location_by_name_or_id(image['id'], args[1])
|
819
|
+
return 1, "location not found for '#{args[1]}'" if location.nil?
|
820
|
+
params.merge!(parse_query_options(options))
|
821
|
+
@virtual_images_interface.setopts(options)
|
822
|
+
if options[:dry_run]
|
823
|
+
print_dry_run @virtual_images_interface.dry.get_location(image['id'], location['id'])
|
824
|
+
return 0, nil
|
825
|
+
end
|
826
|
+
# json_response = @virtual_images_interface.get(image['id'], location['id'])
|
827
|
+
json_response = {'location' => location} # skip redundant request
|
828
|
+
render_response(json_response, options, 'location') do
|
829
|
+
location = json_response['location']
|
830
|
+
volumes = location['volumes'] || []
|
831
|
+
print_h1 "Virtual Image Location Details", [], options
|
832
|
+
print_description_list(virtual_image_location_column_definitions, location, options)
|
833
|
+
if volumes && !volumes.empty?
|
834
|
+
print_h2 "Volumes", options
|
835
|
+
volume_rows = location_volumes.collect do |volume|
|
836
|
+
{name: volume['name'], size: Filesize.from("#{volume['rawSize']} B").pretty}
|
837
|
+
end
|
838
|
+
print cyan
|
839
|
+
print as_pretty_table(volume_rows, [:name, :size], options)
|
840
|
+
print cyan
|
841
|
+
# print "\n", reset
|
842
|
+
end
|
843
|
+
print reset,"\n"
|
844
|
+
end
|
845
|
+
return 0, nil
|
745
846
|
end
|
746
847
|
|
848
|
+
def remove_location(args)
|
849
|
+
params = {}
|
850
|
+
options = {}
|
851
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
852
|
+
opts.banner = subcommand_usage("[image] [location]")
|
853
|
+
opts.on('--remove-from-cloud [true|false]', String, "Remove from cloud. Default is true.") do |val|
|
854
|
+
options[:options]['removeFromCloud'] = ['','true','on'].include?(val.to_s)
|
855
|
+
end
|
856
|
+
build_standard_remove_options(opts, options)
|
857
|
+
opts.footer = <<-EOT
|
858
|
+
Delete a virtual image location.
|
859
|
+
[image] is required. This is the name or id of a virtual image.
|
860
|
+
[location] is required. This is the name or id of a virtual image location.
|
861
|
+
EOT
|
862
|
+
end
|
863
|
+
optparse.parse!(args)
|
864
|
+
verify_args!(args:args, optparse:optparse, count:2)
|
865
|
+
connect(options)
|
866
|
+
image = find_virtual_image_by_name_or_id(args[0])
|
867
|
+
return 1, "virtual image not found for '#{args[0]}'" if image.nil?
|
868
|
+
location = find_virtual_image_location_by_name_or_id(image['id'], args[1])
|
869
|
+
return 1, "location not found for '#{args[1]}'" if location.nil?
|
870
|
+
|
871
|
+
params.merge!(parse_query_options(options))
|
872
|
+
|
873
|
+
# Delete prompt
|
874
|
+
# [ X ] Remove from cloud
|
875
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'removeFromCloud', 'fieldLabel' => 'Remove from cloud', 'type' => 'checkbox', 'defaultValue' => true, 'required' => true, 'description' => "Remove from cloud"}], options[:options], @api_client)
|
876
|
+
remove_from_cloud = v_prompt['removeFromCloud'].to_s == 'true' || v_prompt['removeFromCloud'].to_s == 'on'
|
877
|
+
params['removeFromCloud'] = remove_from_cloud
|
878
|
+
|
879
|
+
# Delete confirmation
|
880
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the virtual image location #{location['id']}?")
|
881
|
+
return 9, "aborted"
|
882
|
+
end
|
883
|
+
|
884
|
+
@virtual_images_interface.setopts(options)
|
885
|
+
if options[:dry_run]
|
886
|
+
print_dry_run @virtual_images_interface.dry.destroy_location(image['id'], location['id'], params)
|
887
|
+
return
|
888
|
+
end
|
889
|
+
json_response = @virtual_images_interface.destroy_location(image['id'], location['id'], params)
|
890
|
+
render_response(json_response, options) do
|
891
|
+
print_green_success "Removed virtual image location #{location['id']}"
|
892
|
+
end
|
893
|
+
return 0, nil
|
894
|
+
end
|
747
895
|
|
748
896
|
private
|
749
|
-
|
897
|
+
|
898
|
+
def find_virtual_image_by_name_or_id(val)
|
750
899
|
if val.to_s =~ /\A\d{1,}\Z/
|
751
900
|
return find_virtual_image_by_id(val)
|
752
901
|
else
|
@@ -911,4 +1060,75 @@ EOT
|
|
911
1060
|
out
|
912
1061
|
end
|
913
1062
|
|
1063
|
+
|
1064
|
+
## Virtual Image Locations
|
1065
|
+
|
1066
|
+
def virtual_image_location_object_key
|
1067
|
+
"location"
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
def virtual_image_location_list_key
|
1071
|
+
"locations"
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
def find_virtual_image_location_by_name_or_id(virtual_image_id, val)
|
1075
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
1076
|
+
return find_virtual_image_location_by_id(virtual_image_id, val)
|
1077
|
+
else
|
1078
|
+
return find_virtual_image_location_by_name(virtual_image_id, val)
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
def virtual_image_location_list_column_definitions
|
1083
|
+
virtual_image_location_column_definitions
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
def virtual_image_location_column_definitions
|
1087
|
+
{
|
1088
|
+
"ID" => 'id',
|
1089
|
+
"Name" => 'imageName',
|
1090
|
+
"Cloud" => lambda {|it| it['cloud']['name'] rescue '' },
|
1091
|
+
"Public" => lambda {|it| format_boolean(it['isPublic']) },
|
1092
|
+
"Region" => lambda {|it| it['imageRegion'] },
|
1093
|
+
"External ID" => lambda {|it| it['externalId'] },
|
1094
|
+
"Price Plan" => lambda {|it| it['pricePlan'] ? it['pricePlan']['name'] : nil },
|
1095
|
+
# "Virtual Image" => lambda {|it| it['virtualImage']['name'] rescue '' },
|
1096
|
+
# "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
1097
|
+
# "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
1098
|
+
}
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
|
1102
|
+
def find_virtual_image_location_by_id(virtual_image_id, id)
|
1103
|
+
begin
|
1104
|
+
json_response = @virtual_images_interface.get_location(virtual_image_id, id.to_i)
|
1105
|
+
return json_response[virtual_image_location_object_key]
|
1106
|
+
rescue RestClient::Exception => e
|
1107
|
+
if e.response && e.response.code == 404
|
1108
|
+
print_red_alert "Virtual Image Location not found by id '#{id}'"
|
1109
|
+
else
|
1110
|
+
raise e
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
def find_virtual_image_location_by_name(virtual_image_id, name)
|
1116
|
+
json_response = @virtual_images_interface.list_locations(virtual_image_id, {imageName: name.to_s})
|
1117
|
+
virtual_image_locations = json_response[virtual_image_location_list_key]
|
1118
|
+
if virtual_image_locations.empty?
|
1119
|
+
print_red_alert "Virtual Image Location not found by name '#{name}'"
|
1120
|
+
return nil
|
1121
|
+
elsif virtual_image_locations.size > 1
|
1122
|
+
print_red_alert "#{virtual_image_locations.size} Virtual Image Locations found by name '#{name}'"
|
1123
|
+
print_error "\n"
|
1124
|
+
puts_error as_pretty_table(virtual_image_locations, {"ID" => 'id', "NAME" => 'imageName'}, {color:red})
|
1125
|
+
print_red_alert "Try using ID instead"
|
1126
|
+
print_error reset,"\n"
|
1127
|
+
return nil
|
1128
|
+
else
|
1129
|
+
return virtual_image_locations[0]
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
|
914
1134
|
end
|