morpheus-cli 5.3.2.3 → 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/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
|