morpheus-cli 9.0.0 → 9.0.2
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/api_client.rb +4 -0
- data/lib/morpheus/api/clusters_interface.rb +72 -0
- data/lib/morpheus/api/instances_interface.rb +1 -1
- data/lib/morpheus/api/options_interface.rb +1 -1
- data/lib/morpheus/api/servers_interface.rb +8 -0
- data/lib/morpheus/api/storage_volumes_interface.rb +8 -0
- data/lib/morpheus/api/virtual_images_interface.rb +10 -2
- data/lib/morpheus/api/virtual_switches_interface.rb +9 -0
- data/lib/morpheus/cli/commands/backups_command.rb +20 -6
- data/lib/morpheus/cli/commands/clusters.rb +575 -14
- data/lib/morpheus/cli/commands/hosts.rb +72 -1
- data/lib/morpheus/cli/commands/instances.rb +13 -2
- data/lib/morpheus/cli/commands/networks_command.rb +35 -3
- data/lib/morpheus/cli/commands/storage_volumes.rb +77 -2
- data/lib/morpheus/cli/commands/virtual_switches_command.rb +336 -0
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +6 -4
- data/lib/morpheus/cli/version.rb +1 -1
- data/test/cli/storage_volumes_test.rb +79 -0
- metadata +6 -2
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
|
2
|
+
|
|
3
|
+
# CLI command Virtual Switch management
|
|
4
|
+
# UI is Tools: Virtual Switches
|
|
5
|
+
# API is /api/virtual-switches and returns virtualSwitches
|
|
6
|
+
class Morpheus::Cli::VirtualSwitchesCommand
|
|
7
|
+
include Morpheus::Cli::CliCommand
|
|
8
|
+
include Morpheus::Cli::OptionSourceHelper
|
|
9
|
+
|
|
10
|
+
set_command_name :'virtual-switches'
|
|
11
|
+
set_command_description "View and manage Virtual Switches"
|
|
12
|
+
set_command_hidden
|
|
13
|
+
register_subcommands :list, :get, :add, :update, :remove
|
|
14
|
+
|
|
15
|
+
def connect(opts)
|
|
16
|
+
@api_client = establish_remote_appliance_connection(opts)
|
|
17
|
+
@virtual_switches_interface = @api_client.virtual_switches
|
|
18
|
+
@clusters_interface = @api_client.clusters
|
|
19
|
+
@networks_interface = @api_client.networks
|
|
20
|
+
@options_interface = @api_client.options
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def handle(args)
|
|
25
|
+
handle_subcommand(args)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def list(args)
|
|
29
|
+
options = {}
|
|
30
|
+
params = {}
|
|
31
|
+
ref_ids = []
|
|
32
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
33
|
+
opts.banner = subcommand_usage("[search]")
|
|
34
|
+
opts.on( '--enabled [on|off]', String, "Filter by enabled" ) do |val|
|
|
35
|
+
params['enabled'] = (val.to_s != 'false' && val.to_s != 'off')
|
|
36
|
+
end
|
|
37
|
+
build_standard_list_options(opts, options)
|
|
38
|
+
opts.footer = "List Virtual Switches."
|
|
39
|
+
end
|
|
40
|
+
optparse.parse!(args)
|
|
41
|
+
connect(options)
|
|
42
|
+
if args.count > 0
|
|
43
|
+
options[:phrase] = args.join(" ")
|
|
44
|
+
end
|
|
45
|
+
params.merge!(parse_list_options(options))
|
|
46
|
+
@virtual_switches_interface.setopts(options)
|
|
47
|
+
if options[:dry_run]
|
|
48
|
+
print_dry_run @virtual_switches_interface.dry.list(params)
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
json_response = @virtual_switches_interface.list(params)
|
|
52
|
+
render_response(json_response, options, virtual_switch_list_key) do
|
|
53
|
+
virtual_switches = json_response[virtual_switch_list_key]
|
|
54
|
+
print_h1 "Morpheus Virtual Switches", parse_list_subtitles(options), options
|
|
55
|
+
if virtual_switches.empty?
|
|
56
|
+
print cyan,"No Virtual Switches found.",reset,"\n"
|
|
57
|
+
else
|
|
58
|
+
columns = {
|
|
59
|
+
"ID" => 'id',
|
|
60
|
+
"Name" => 'name',
|
|
61
|
+
"Type" => lambda {|it| it['type']['name'] rescue it['baseType'] },
|
|
62
|
+
"NIC Count" => lambda {|it| it['nicCount'] },
|
|
63
|
+
"Network Count" => lambda {|it| it['networkCount'] },
|
|
64
|
+
}
|
|
65
|
+
print as_pretty_table(virtual_switches, columns.upcase_keys!, options)
|
|
66
|
+
print_results_pagination(json_response)
|
|
67
|
+
end
|
|
68
|
+
print reset,"\n"
|
|
69
|
+
end
|
|
70
|
+
return 0, nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def get(args)
|
|
74
|
+
params = {}
|
|
75
|
+
options = {}
|
|
76
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
77
|
+
opts.banner = subcommand_usage("[switch]")
|
|
78
|
+
build_standard_get_options(opts, options)
|
|
79
|
+
opts.footer = <<-EOT
|
|
80
|
+
Get details about a specific Virtual Switch.
|
|
81
|
+
[switch] is required. This is the name or id of a Virtual Switch.
|
|
82
|
+
EOT
|
|
83
|
+
end
|
|
84
|
+
optparse.parse!(args)
|
|
85
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
|
86
|
+
connect(options)
|
|
87
|
+
params.merge!(parse_query_options(options))
|
|
88
|
+
id_list = parse_id_list(args)
|
|
89
|
+
return run_command_for_each_arg(id_list) do |arg|
|
|
90
|
+
_get(arg, params, options)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def _get(id, params, options)
|
|
95
|
+
virtual_switch = nil
|
|
96
|
+
if id.to_s !~ /\A\d{1,}\Z/
|
|
97
|
+
virtual_switch = find_virtual_switch_by_name(id)
|
|
98
|
+
return 1, "Virtual Switch not found for #{id}" if virtual_switch.nil?
|
|
99
|
+
id = virtual_switch['id']
|
|
100
|
+
end
|
|
101
|
+
@virtual_switches_interface.setopts(options)
|
|
102
|
+
if options[:dry_run]
|
|
103
|
+
print_dry_run @virtual_switches_interface.dry.get(id, params)
|
|
104
|
+
return
|
|
105
|
+
end
|
|
106
|
+
json_response = @virtual_switches_interface.get(id, params)
|
|
107
|
+
virtual_switch = json_response[virtual_switch_object_key]
|
|
108
|
+
render_response(json_response, options, virtual_switch_object_key) do
|
|
109
|
+
print_h1 "Virtual Switch Details", [], options
|
|
110
|
+
print cyan
|
|
111
|
+
columns = {
|
|
112
|
+
"ID" => 'id',
|
|
113
|
+
"Name" => 'name',
|
|
114
|
+
"Type" => lambda {|it| it['type']['name'] rescue it['baseType'] },
|
|
115
|
+
"NIC Count" => lambda {|it| it['nicCount'] },
|
|
116
|
+
"Network Count" => lambda {|it| it['networkCount'] },
|
|
117
|
+
}
|
|
118
|
+
print_description_list(columns, virtual_switch, options)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
print reset,"\n"
|
|
122
|
+
end
|
|
123
|
+
return 0, nil
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def add(args)
|
|
127
|
+
options = {}
|
|
128
|
+
params = {}
|
|
129
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
130
|
+
opts.banner = subcommand_usage("[name] [options]")
|
|
131
|
+
build_option_type_options(opts, options, add_virtual_switch_option_types)
|
|
132
|
+
build_standard_add_options(opts, options)
|
|
133
|
+
opts.footer = <<-EOT
|
|
134
|
+
Create a new Virtual Switch.
|
|
135
|
+
EOT
|
|
136
|
+
end
|
|
137
|
+
optparse.parse!(args)
|
|
138
|
+
verify_args!(args:args, optparse:optparse, min:0, max:1)
|
|
139
|
+
options[:options]['name'] = args[0] if args[0]
|
|
140
|
+
if options[:options]['logo']
|
|
141
|
+
options[:options]['iconPath'] = 'custom'
|
|
142
|
+
end
|
|
143
|
+
connect(options)
|
|
144
|
+
payload = {}
|
|
145
|
+
if options[:payload]
|
|
146
|
+
payload = options[:payload]
|
|
147
|
+
payload.deep_merge!({virtual_switch_object_key => parse_passed_options(options)})
|
|
148
|
+
else
|
|
149
|
+
params.deep_merge!(parse_passed_options(options))
|
|
150
|
+
# prompt for option types
|
|
151
|
+
option_types = add_virtual_switch_option_types
|
|
152
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
|
|
153
|
+
params.deep_merge!(v_prompt)
|
|
154
|
+
# convert checkbox "on" and "off" to true and false
|
|
155
|
+
params.booleanize!
|
|
156
|
+
|
|
157
|
+
# massage association params a bit
|
|
158
|
+
# params['apps'] = ...
|
|
159
|
+
payload.deep_merge!({virtual_switch_object_key => params})
|
|
160
|
+
end
|
|
161
|
+
@virtual_switches_interface.setopts(options)
|
|
162
|
+
if options[:dry_run]
|
|
163
|
+
print_dry_run @virtual_switches_interface.dry.create(payload)
|
|
164
|
+
return 0, nil
|
|
165
|
+
end
|
|
166
|
+
json_response = @virtual_switches_interface.create(payload)
|
|
167
|
+
virtual_switch = json_response[virtual_switch_object_key]
|
|
168
|
+
render_response(json_response, options, virtual_switch_object_key) do
|
|
169
|
+
print_green_success "Added virtual switch #{virtual_switch['name']}"
|
|
170
|
+
return _get(virtual_switch["id"], {}, options)
|
|
171
|
+
end
|
|
172
|
+
return 0, nil
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def update(args)
|
|
176
|
+
options = {}
|
|
177
|
+
params = {}
|
|
178
|
+
payload = {}
|
|
179
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
180
|
+
opts.banner = subcommand_usage("[switch] [options]")
|
|
181
|
+
build_option_type_options(opts, options, update_virtual_switch_option_types)
|
|
182
|
+
build_standard_update_options(opts, options)
|
|
183
|
+
opts.footer = <<-EOT
|
|
184
|
+
Update a virtual switch.
|
|
185
|
+
[switch] is required. This is the name or id of a virtual switch.
|
|
186
|
+
EOT
|
|
187
|
+
end
|
|
188
|
+
optparse.parse!(args)
|
|
189
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
|
190
|
+
if options[:options]['logo']
|
|
191
|
+
options[:options]['iconPath'] = 'custom'
|
|
192
|
+
end
|
|
193
|
+
connect(options)
|
|
194
|
+
virtual_switch = find_virtual_switch_by_name_or_id(args[0])
|
|
195
|
+
return 1 if virtual_switch.nil?
|
|
196
|
+
payload = {}
|
|
197
|
+
if options[:payload]
|
|
198
|
+
payload = options[:payload]
|
|
199
|
+
payload.deep_merge!({virtual_switch_object_key => parse_passed_options(options)})
|
|
200
|
+
else
|
|
201
|
+
params.deep_merge!(parse_passed_options(options))
|
|
202
|
+
# do not prompt on update
|
|
203
|
+
v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_virtual_switch_option_types, options[:options], @api_client, options[:params])
|
|
204
|
+
v_prompt.deep_compact!
|
|
205
|
+
params.deep_merge!(v_prompt)
|
|
206
|
+
# convert checkbox "on" and "off" to true and false
|
|
207
|
+
params.booleanize!
|
|
208
|
+
# massage association params a bit
|
|
209
|
+
|
|
210
|
+
# params['apps'] = ...
|
|
211
|
+
payload.deep_merge!({virtual_switch_object_key => params})
|
|
212
|
+
if payload[virtual_switch_object_key].empty? # || options[:no_prompt]
|
|
213
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
@virtual_switches_interface.setopts(options)
|
|
217
|
+
if options[:dry_run]
|
|
218
|
+
print_dry_run @virtual_switches_interface.dry.update(virtual_switch['id'], payload)
|
|
219
|
+
return
|
|
220
|
+
end
|
|
221
|
+
json_response = @virtual_switches_interface.update(virtual_switch['id'], payload)
|
|
222
|
+
virtual_switch = json_response[virtual_switch_object_key]
|
|
223
|
+
render_response(json_response, options, virtual_switch_object_key) do
|
|
224
|
+
print_green_success "Updated virtual switch #{virtual_switch['name']}"
|
|
225
|
+
return _get(virtual_switch["id"], {}, options)
|
|
226
|
+
end
|
|
227
|
+
return 0, nil
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def remove(args)
|
|
231
|
+
options = {}
|
|
232
|
+
params = {}
|
|
233
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
234
|
+
opts.banner = subcommand_usage("[switch] [options]")
|
|
235
|
+
build_standard_remove_options(opts, options)
|
|
236
|
+
opts.footer = <<-EOT
|
|
237
|
+
Delete a virtual switch.
|
|
238
|
+
[switch] is required. This is the name or id of a virtual switch.
|
|
239
|
+
EOT
|
|
240
|
+
end
|
|
241
|
+
optparse.parse!(args)
|
|
242
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
|
243
|
+
connect(options)
|
|
244
|
+
virtual_switch = find_virtual_switch_by_name_or_id(args[0])
|
|
245
|
+
return 1 if virtual_switch.nil?
|
|
246
|
+
@virtual_switches_interface.setopts(options)
|
|
247
|
+
if options[:dry_run]
|
|
248
|
+
print_dry_run @virtual_switches_interface.dry.destroy(virtual_switch['id'], params)
|
|
249
|
+
return
|
|
250
|
+
end
|
|
251
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the virtual switch #{virtual_switch['name']}?")
|
|
252
|
+
return 9, "aborted command"
|
|
253
|
+
end
|
|
254
|
+
json_response = @virtual_switches_interface.destroy(virtual_switch['id'], params)
|
|
255
|
+
render_response(json_response, options) do
|
|
256
|
+
print_green_success "Removed virtual switch #{virtual_switch['name']}"
|
|
257
|
+
end
|
|
258
|
+
return 0, nil
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
private
|
|
262
|
+
|
|
263
|
+
def add_virtual_switch_option_types
|
|
264
|
+
[
|
|
265
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Choose a unique name for the Virtual Switch'},
|
|
266
|
+
#{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'description' => 'Description'},
|
|
267
|
+
{'switch' => '-t', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'description' => 'Type of virtual switch', 'selectOptions' => [{'name' => 'VM Network', 'value' => 'general'}, {'name' => 'ISCSI', 'value' => 'iscsi'}, {'name' => 'NFS', 'value' => 'nfs'}, {'name' => 'Live Migration', 'value' => 'live_migration'}, {'name' => 'SDN', 'value' => 'sdn'}], 'required' => true},
|
|
268
|
+
]
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def update_virtual_switch_option_types
|
|
272
|
+
list = add_virtual_switch_option_types.collect {|it|
|
|
273
|
+
it.delete('required')
|
|
274
|
+
it.delete('defaultValue')
|
|
275
|
+
it
|
|
276
|
+
}
|
|
277
|
+
list = list.reject {|it| ["type"].include? it['fieldName'] }
|
|
278
|
+
list
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def find_virtual_switch_by_name_or_id(val)
|
|
282
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
|
283
|
+
return find_virtual_switch_by_id(val)
|
|
284
|
+
else
|
|
285
|
+
return find_virtual_switch_by_name(val)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def find_virtual_switch_by_id(id)
|
|
290
|
+
begin
|
|
291
|
+
json_response = virtual_switches_interface.get(id.to_i)
|
|
292
|
+
return json_response[virtual_switch_object_key]
|
|
293
|
+
rescue RestClient::Exception => e
|
|
294
|
+
if e.response && e.response.code == 404
|
|
295
|
+
print_red_alert "Virtual Switch not found by id '#{id}'"
|
|
296
|
+
else
|
|
297
|
+
raise e
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def find_virtual_switch_by_name(name)
|
|
303
|
+
json_response = virtual_switches_interface.list({name: name.to_s})
|
|
304
|
+
virtual_switches = json_response[virtual_switch_list_key]
|
|
305
|
+
if virtual_switches.empty?
|
|
306
|
+
print_red_alert "Virtual Switch not found by name '#{name}'"
|
|
307
|
+
return nil
|
|
308
|
+
elsif virtual_switches.size > 1
|
|
309
|
+
print_red_alert "#{virtual_switches.size} Virtual Switches found by name '#{name}'"
|
|
310
|
+
print_error "\n"
|
|
311
|
+
puts_error as_pretty_table(virtual_switches, [:id, :name], {color:red})
|
|
312
|
+
print_red_alert "Try using ID instead"
|
|
313
|
+
print_error reset,"\n"
|
|
314
|
+
return nil
|
|
315
|
+
else
|
|
316
|
+
return virtual_switches[0]
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def format_virtual_switch_status(virtual_switch, return_color=cyan)
|
|
321
|
+
out = ""
|
|
322
|
+
status_string = virtual_switch['status'].to_s.downcase
|
|
323
|
+
if status_string
|
|
324
|
+
if ['available','ok'].include?(status_string)
|
|
325
|
+
out << "#{green}#{status_string.upcase}"
|
|
326
|
+
elsif ['unavailable','error'].include?(status_string)
|
|
327
|
+
out << "#{red}#{status_string.upcase}"
|
|
328
|
+
else
|
|
329
|
+
out << "#{return_color}#{status_string.upcase}"
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
out + return_color
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
end
|
|
@@ -166,11 +166,11 @@ module Morpheus::Cli::InfrastructureHelper
|
|
|
166
166
|
|
|
167
167
|
# Networks
|
|
168
168
|
|
|
169
|
-
def find_network_by_name_or_id(val)
|
|
169
|
+
def find_network_by_name_or_id(val, include_tenants=false)
|
|
170
170
|
if val.to_s =~ /\A\d{1,}\Z/
|
|
171
171
|
return find_network_by_id(val)
|
|
172
172
|
else
|
|
173
|
-
return find_network_by_name(val)
|
|
173
|
+
return find_network_by_name(val, include_tenants)
|
|
174
174
|
end
|
|
175
175
|
end
|
|
176
176
|
|
|
@@ -188,8 +188,10 @@ module Morpheus::Cli::InfrastructureHelper
|
|
|
188
188
|
end
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
-
def find_network_by_name(name)
|
|
192
|
-
|
|
191
|
+
def find_network_by_name(name, include_tenants=false)
|
|
192
|
+
params = {name: name.to_s}
|
|
193
|
+
params['includeTenants'] = true if include_tenants
|
|
194
|
+
json_response = networks_interface.list(params)
|
|
193
195
|
networks = json_response['networks']
|
|
194
196
|
if networks.empty?
|
|
195
197
|
print_red_alert "Network not found by name #{name}"
|
data/lib/morpheus/cli/version.rb
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'morpheus_test'
|
|
2
|
+
|
|
3
|
+
# Tests for Morpheus::Cli::StorageVolumes
|
|
4
|
+
class MorpheusTest::StorageVolumesTest < MorpheusTest::TestCase
|
|
5
|
+
|
|
6
|
+
def test_storage_volumes_list
|
|
7
|
+
assert_execute %(storage-volumes list)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_storage_volumes_get
|
|
11
|
+
storage_volume = client.storage_volumes.list({})['storageVolumes'][0]
|
|
12
|
+
if storage_volume
|
|
13
|
+
assert_execute %(storage-volumes get "#{storage_volume['id']}")
|
|
14
|
+
else
|
|
15
|
+
puts "No storage volumes found, unable to execute test `#{__method__}`"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# CLI-side validation: --tenant is required (no network call needed).
|
|
20
|
+
def test_storage_volumes_update_tenant_requires_tenant
|
|
21
|
+
assert_error %(storage-volumes update-tenant 1), "Expected missing --tenant to fail with a usage error"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Happy path is verified with --dry-run so the test never mutates real ownership.
|
|
25
|
+
# Confirms the volume is resolved and the PUT payload is built as expected.
|
|
26
|
+
def test_storage_volumes_update_tenant_dry_run
|
|
27
|
+
storage_volume = client.storage_volumes.list({})['storageVolumes'].find {|it| it['status'] == 'unattached' }
|
|
28
|
+
storage_volume ||= client.storage_volumes.list({})['storageVolumes'][0]
|
|
29
|
+
if storage_volume
|
|
30
|
+
tenant = client.accounts.list({})['accounts'][0]
|
|
31
|
+
if tenant
|
|
32
|
+
assert_execute %(storage-volumes update-tenant "#{storage_volume['id']}" --tenant "#{tenant['id']}" --dry-run)
|
|
33
|
+
else
|
|
34
|
+
puts "No tenants found, unable to execute test `#{__method__}`"
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
puts "No storage volumes found, unable to execute test `#{__method__}`"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# --include-tenants adds includeTenants=true to the list request (master tenant only).
|
|
42
|
+
def test_storage_volumes_list_include_tenants_dry_run
|
|
43
|
+
assert_execute %(storage-volumes list --include-tenants --dry-run)
|
|
44
|
+
output = capture_terminal_stdout { terminal.execute %(storage-volumes list --include-tenants --dry-run) }
|
|
45
|
+
assert_match(/includeTenants=true/, output, "Expected includeTenants=true in the dry-run request")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# --tenant with a numeric id resolves directly to tenantId without an account lookup.
|
|
49
|
+
def test_storage_volumes_list_tenant_id_dry_run
|
|
50
|
+
assert_execute %(storage-volumes list --tenant 1 --dry-run)
|
|
51
|
+
output = capture_terminal_stdout { terminal.execute %(storage-volumes list --tenant 1 --dry-run) }
|
|
52
|
+
assert_match(/tenantId=1/, output, "Expected tenantId=1 in the dry-run request")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# --tenant with a name resolves to an id via the accounts API, then sets tenantId.
|
|
56
|
+
def test_storage_volumes_list_tenant_name_dry_run
|
|
57
|
+
tenant = client.accounts.list({})['accounts'][0]
|
|
58
|
+
if tenant
|
|
59
|
+
output = capture_terminal_stdout { terminal.execute %(storage-volumes list --tenant "#{tenant['name']}" --dry-run) }
|
|
60
|
+
assert_match(/tenantId=#{tenant['id']}/, output, "Expected tenantId=#{tenant['id']} resolved from tenant name")
|
|
61
|
+
else
|
|
62
|
+
puts "No tenants found, unable to execute test `#{__method__}`"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# Capture everything written to the terminal's stdout while the block runs.
|
|
69
|
+
def capture_terminal_stdout
|
|
70
|
+
original_stdout = terminal.stdout
|
|
71
|
+
buffer = StringIO.new
|
|
72
|
+
terminal.set_stdout(buffer)
|
|
73
|
+
yield
|
|
74
|
+
buffer.string
|
|
75
|
+
ensure
|
|
76
|
+
terminal.set_stdout(original_stdout)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: morpheus-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.0.
|
|
4
|
+
version: 9.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Estes
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2026-
|
|
14
|
+
date: 2026-08-26 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: tins
|
|
@@ -399,6 +399,7 @@ files:
|
|
|
399
399
|
- lib/morpheus/api/vdi_pools_interface.rb
|
|
400
400
|
- lib/morpheus/api/virtual_images_interface.rb
|
|
401
401
|
- lib/morpheus/api/virtual_servers_interface.rb
|
|
402
|
+
- lib/morpheus/api/virtual_switches_interface.rb
|
|
402
403
|
- lib/morpheus/api/whitelabel_settings_interface.rb
|
|
403
404
|
- lib/morpheus/api/whoami_interface.rb
|
|
404
405
|
- lib/morpheus/api/wiki_interface.rb
|
|
@@ -594,6 +595,7 @@ files:
|
|
|
594
595
|
- lib/morpheus/cli/commands/version_command.rb
|
|
595
596
|
- lib/morpheus/cli/commands/view.rb
|
|
596
597
|
- lib/morpheus/cli/commands/virtual_images.rb
|
|
598
|
+
- lib/morpheus/cli/commands/virtual_switches_command.rb
|
|
597
599
|
- lib/morpheus/cli/commands/whitelabel_settings_command.rb
|
|
598
600
|
- lib/morpheus/cli/commands/whoami.rb
|
|
599
601
|
- lib/morpheus/cli/commands/wiki_command.rb
|
|
@@ -662,6 +664,7 @@ files:
|
|
|
662
664
|
- test/cli/remote_test.rb
|
|
663
665
|
- test/cli/roles_test.rb
|
|
664
666
|
- test/cli/shell_test.rb
|
|
667
|
+
- test/cli/storage_volumes_test.rb
|
|
665
668
|
- test/cli/systems_test.rb
|
|
666
669
|
- test/cli/version_test.rb
|
|
667
670
|
- test/cli/view_test.rb
|
|
@@ -713,6 +716,7 @@ test_files:
|
|
|
713
716
|
- test/cli/remote_test.rb
|
|
714
717
|
- test/cli/roles_test.rb
|
|
715
718
|
- test/cli/shell_test.rb
|
|
719
|
+
- test/cli/storage_volumes_test.rb
|
|
716
720
|
- test/cli/systems_test.rb
|
|
717
721
|
- test/cli/version_test.rb
|
|
718
722
|
- test/cli/view_test.rb
|