morpheus-cli 5.3.2.3 → 5.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/instances_interface.rb +18 -5
- 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 +12 -5
- 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 +253 -137
- data/lib/morpheus/cli/invoices_command.rb +79 -99
- data/lib/morpheus/cli/library_cluster_layouts_command.rb +20 -0
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +120 -94
- data/lib/morpheus/cli/monitoring_checks_command.rb +2 -0
- data/lib/morpheus/cli/networks_command.rb +1 -1
- data/lib/morpheus/cli/option_parser.rb +25 -17
- data/lib/morpheus/cli/option_types.rb +22 -15
- 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/morpheus-cli.gemspec +1 -1
- metadata +4 -4
@@ -158,11 +158,13 @@ class Morpheus::Cli::MonitoringChecksCommand
|
|
158
158
|
end
|
159
159
|
},
|
160
160
|
"Type" => lambda {|it| format_monitoring_check_type(it) },
|
161
|
+
"API Key" => lambda {|it| it['apiKey'] },
|
161
162
|
"Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : "System" },
|
162
163
|
"Date Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
163
164
|
"Last Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
164
165
|
# "Last Error" => lambda {|it| format_local_dt(it['lastErrorDate']) },
|
165
166
|
}
|
167
|
+
description_cols.delete("API Key") if !['pushCheck'].include?(check['checkType']['code']) || check['apiKey'].nil?
|
166
168
|
print_description_list(description_cols, check)
|
167
169
|
|
168
170
|
# Last Error
|
@@ -619,7 +619,7 @@ class Morpheus::Cli::NetworksCommand
|
|
619
619
|
api_params['network.site.id'] = group ? group['id'] : 'shared'
|
620
620
|
api_params['network.type.id'] = network_type['id']
|
621
621
|
api_params['network.networkServer.id'] = network_server_id if !network_server_id.nil?
|
622
|
-
network_type_params = Morpheus::Cli::OptionTypes.prompt(network_type_option_types,options[:options],@api_client, api_params)
|
622
|
+
network_type_params = Morpheus::Cli::OptionTypes.prompt(network_type_option_types,(options[:options] || {}).merge(payload),@api_client, api_params)
|
623
623
|
# network context options belong at network level and not network.network
|
624
624
|
network_context_params = network_type_params.delete('network')
|
625
625
|
payload['network'].deep_merge!(network_context_params) if network_context_params
|
@@ -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
|
|
@@ -102,10 +102,10 @@ module Morpheus
|
|
102
102
|
end
|
103
103
|
|
104
104
|
found_dep_value = match_type == 'all' ? true : false
|
105
|
-
visible_option_check_value.
|
105
|
+
visible_option_check_value.sub(',', ' ').split(' ').each do |value|
|
106
106
|
parts = value.split(':')
|
107
107
|
depends_on_code = parts[0]
|
108
|
-
depends_on_value = parts.count > 1 ? parts[1].to_s.strip :
|
108
|
+
depends_on_value = parts.count > 1 ? parts[1].to_s.strip : nil
|
109
109
|
depends_on_option_type = option_types.find {|it| it["code"] == depends_on_code }
|
110
110
|
if !depends_on_option_type
|
111
111
|
depends_on_option_type = option_types.find {|it|
|
@@ -115,13 +115,18 @@ module Morpheus
|
|
115
115
|
|
116
116
|
if depends_on_option_type
|
117
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']}"
|
118
|
-
|
118
|
+
else
|
119
|
+
depends_on_field_key = depends_on_code
|
120
|
+
end
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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'
|
125
130
|
end
|
126
131
|
end
|
127
132
|
next if !found_dep_value
|
@@ -237,7 +242,7 @@ module Morpheus
|
|
237
242
|
# I suppose the entered value should take precedence
|
238
243
|
# api_params = api_params.merge(options) # this might be good enough
|
239
244
|
# dup it
|
240
|
-
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)
|
241
246
|
if value && option_type['type'] == 'multiSelect'
|
242
247
|
value = [value]
|
243
248
|
while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
|
@@ -276,7 +281,8 @@ module Morpheus
|
|
276
281
|
value = [value] if !value.nil? && !value.is_a?(Array)
|
277
282
|
# parent_context_map[parent_ns] = value
|
278
283
|
end
|
279
|
-
context_map[field_name] = value
|
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?))}
|
280
286
|
end
|
281
287
|
results
|
282
288
|
end
|
@@ -947,8 +953,9 @@ module Morpheus
|
|
947
953
|
out << "\n"
|
948
954
|
out << "#{header}\n"
|
949
955
|
out << "#{'=' * header.length}\n"
|
956
|
+
|
950
957
|
select_options.each do |option|
|
951
|
-
out << " * #{option['name']} [#{option['value']}]\n"
|
958
|
+
out << (option['isGroup'] ? "- #{option['name']}\n" : " * #{option['name']} [#{option['value']}]\n")
|
952
959
|
end
|
953
960
|
return out
|
954
961
|
end
|
@@ -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
|