morpheus-cli 9.0.1 → 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.
@@ -15,7 +15,7 @@ class Morpheus::Cli::Hosts
15
15
  :wiki, :update_wiki,
16
16
  :maintenance, :leave_maintenance, :placement,
17
17
  :list_devices, :assign_device, :detach_device, :attach_device,
18
- :snapshot
18
+ :snapshot, :create_linked_clone
19
19
  alias_subcommand :details, :get
20
20
  set_default_subcommand :list
21
21
 
@@ -796,6 +796,10 @@ class Morpheus::Cli::Hosts
796
796
  opts.on("--security-groups LIST", Integer, "Security Groups, comma separated list of security group IDs") do |val|
797
797
  options[:security_groups] = val.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
798
798
  end
799
+ opts.on("--storage-controller ARRAY", String, "Storage Controllers, JSON array of controller configs. Each entry accepts id, busNumber, typeId, typeName, removable, editable.") do |val|
800
+ options[:options] ||= {}
801
+ options[:options]['storageController'] = JSON.parse(val)
802
+ end
799
803
  opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
800
804
  options[:metadata] = val
801
805
  end
@@ -956,6 +960,10 @@ class Morpheus::Cli::Hosts
956
960
  payload['volumes'] = volumes
957
961
  end
958
962
 
963
+ if options[:options] && options[:options]['storageController']
964
+ payload['storageController'] = options[:options]['storageController']
965
+ end
966
+
959
967
  # plan customizations
960
968
  plan_opts = prompt_service_plan_options(service_plan, options, @api_client, {})
961
969
  if plan_opts && !plan_opts.empty?
@@ -1347,6 +1355,10 @@ class Morpheus::Cli::Hosts
1347
1355
  options = {}
1348
1356
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1349
1357
  opts.banner = subcommand_usage("[name]")
1358
+ opts.on("--storage-controller ARRAY", String, "Storage Controllers, JSON array of controller configs. Each entry accepts id, busNumber, typeId, typeName, removable, editable.") do |val|
1359
+ options[:options] ||= {}
1360
+ options[:options]['storageController'] = JSON.parse(val)
1361
+ end
1350
1362
  build_common_options(opts, options, [:options, :json, :dry_run, :quiet, :remote])
1351
1363
  end
1352
1364
  optparse.parse!(args)
@@ -1435,6 +1447,9 @@ class Morpheus::Cli::Hosts
1435
1447
  # only amazon supports this option
1436
1448
  # for now, always do this
1437
1449
  payload[:deleteOriginalVolumes] = true
1450
+ if options[:options] && options[:options]['storageController']
1451
+ payload[:storageController] = options[:options]['storageController']
1452
+ end
1438
1453
  @servers_interface.setopts(options)
1439
1454
  if options[:dry_run]
1440
1455
  print_dry_run @servers_interface.dry.resize(server['id'], payload)
@@ -2716,6 +2731,62 @@ EOT
2716
2731
  return 0, nil
2717
2732
  end
2718
2733
 
2734
+ def create_linked_clone(args)
2735
+ options = {}
2736
+ server = nil
2737
+ snapshot_id = nil
2738
+
2739
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2740
+ opts.banner = subcommand_usage("[host]")
2741
+ opts.on("--snapshot ID", String, "Snapshot ID") do |val|
2742
+ snapshot_id = val
2743
+ end
2744
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
2745
+ opts.footer = "Create a linked clone using the selected snapshot of a Host." + "\n" +
2746
+ "[snapshotId] is required. This is the id of the snapshot which the clone will refer to."
2747
+ end
2748
+
2749
+ optparse.parse!(args)
2750
+ if args.count != 1
2751
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
2752
+ end
2753
+ connect(options)
2754
+ begin
2755
+ server = find_host_by_name_or_id(args[0])
2756
+ options[:options]['serverId'] = server['id']
2757
+ begin
2758
+ snapshot_options = @servers_interface.snapshots(server['id'], {})['snapshots'].collect {|it| {'name' => "#{it['name']} (#{it['id']})", 'value' => it['id']} }
2759
+ snapshot_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'snapshotId', 'type' => 'select', 'fieldLabel' => 'Snapshot', 'selectOptions' => snapshot_options, 'required' => true, 'description' => 'Select Snapshot.'}], {}, @api_client, options[:options])
2760
+
2761
+ if !snapshot_prompt['snapshotId'].to_s.empty?
2762
+ snapshot_id = snapshot_prompt['snapshotId']
2763
+ end
2764
+ rescue RestClient::Exception => e
2765
+ puts "Failed to load host snapshots"
2766
+ end
2767
+
2768
+ @servers_interface.setopts(options)
2769
+
2770
+ payload = {}
2771
+ if options[:dry_run]
2772
+ print_dry_run @servers_interface.dry.create_linked_clone(server['id'], snapshot_id, payload)
2773
+ return
2774
+ end
2775
+
2776
+ json_response = @servers_interface.create_linked_clone(server['id'], snapshot_id, payload)
2777
+ if options[:json]
2778
+ puts as_json(json_response, options)
2779
+ else
2780
+ print_green_success "Linked Clone creation initiated."
2781
+ end
2782
+ return 0
2783
+
2784
+ rescue RestClient::Exception => e
2785
+ print_rest_exception(e, options)
2786
+ exit 1
2787
+ end
2788
+ end
2789
+
2719
2790
  ## Server Devices
2720
2791
 
2721
2792
  def list_devices(args)
@@ -465,6 +465,10 @@ class Morpheus::Cli::Instances
465
465
  opts.on("--layout-size NUMBER", Integer, "Apply a multiply factor of containers/vms within the instance") do |val|
466
466
  options[:layout_size] = val.to_i
467
467
  end
468
+ opts.on("--storage-controller ARRAY", String, "Storage Controllers, JSON array of controller configs. Each entry accepts id, busNumber, typeId, typeName, removable, editable. Example: '[{\"busNumber\":\"1\",\"typeName\":\"SCSI\"}]'") do |val|
469
+ options[:options] ||= {}
470
+ options[:options]['storageController'] = JSON.parse(val)
471
+ end
468
472
  opts.on( '-l', '--layout LAYOUT', "Layout ID" ) do |val|
469
473
  options[:layout] = val
470
474
  end
@@ -595,6 +599,9 @@ class Morpheus::Cli::Instances
595
599
  end
596
600
 
597
601
  payload['instance'] ||= {}
602
+ if options[:options] && options[:options]['storageController']
603
+ payload['storageController'] = options[:options]['storageController']
604
+ end
598
605
  if options[:instance_name]
599
606
  payload['instance']['name'] = options[:instance_name]
600
607
  end
@@ -2910,6 +2917,10 @@ class Morpheus::Cli::Instances
2910
2917
  opts.on('--include-network-interfaces','--include-network-interfaces', "Populate payload networkInterfaces with current interfaces") do
2911
2918
  options[:include_nics] = true
2912
2919
  end
2920
+ opts.on("--storage-controller ARRAY", String, "Storage Controllers, JSON array of controller configs. Each entry accepts id, busNumber, typeId, typeName, removable, editable.") do |val|
2921
+ options[:options] ||= {}
2922
+ options[:options]['storageController'] = JSON.parse(val)
2923
+ end
2913
2924
  build_standard_update_options(opts, options)
2914
2925
  end
2915
2926
  optparse.parse!(args)
@@ -3958,7 +3969,7 @@ List snapshots for an instance.
3958
3969
 
3959
3970
  optparse = Morpheus::Cli::OptionParser.new do |opts|
3960
3971
  opts.banner = subcommand_usage("[instance]")
3961
- opts.on("--snapshot ID", String, "Optional snapshot") do |val|
3972
+ opts.on("--snapshot ID", String, "Snapshot ID") do |val|
3962
3973
  snapshot_id = val
3963
3974
  end
3964
3975
  build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
@@ -5,6 +5,7 @@ class Morpheus::Cli::NetworksCommand
5
5
  include Morpheus::Cli::WhoamiHelper
6
6
  include Morpheus::Cli::InfrastructureHelper
7
7
  include Morpheus::Cli::ProvisioningHelper
8
+ include Morpheus::Cli::AccountsHelper
8
9
 
9
10
  set_command_name :networks
10
11
 
@@ -28,6 +29,8 @@ class Morpheus::Cli::NetworksCommand
28
29
  @groups_interface = @api_client.groups
29
30
  @clouds_interface = @api_client.clouds
30
31
  @options_interface = @api_client.options
32
+ @accounts_interface = @api_client.accounts
33
+ @account_users_interface = @api_client.account_users
31
34
  end
32
35
 
33
36
  def handle(args)
@@ -57,6 +60,13 @@ class Morpheus::Cli::NetworksCommand
57
60
  opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
58
61
  add_query_parameter(params, 'allLabels', parse_labels(val))
59
62
  end
63
+ opts.on('--include-tenants','--include-tenants', "Include sub tenant networks") do
64
+ options[:include_tenants] = true
65
+ params['includeTenants'] = true
66
+ end
67
+ opts.on('--tenant TENANT', String, "Tenant Name or ID" ) do |val|
68
+ options[:tenant] = val
69
+ end
60
70
  build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :json, :dry_run, :remote])
61
71
  opts.footer = "List networks."
62
72
  end
@@ -73,6 +83,18 @@ class Morpheus::Cli::NetworksCommand
73
83
  return 1 if cloud.nil?
74
84
  params['zoneId'] = cloud['id']
75
85
  end
86
+ if options[:tenant]
87
+ if options[:tenant].to_s !~ /\A\d{1,}\Z/
88
+ account = find_account_by_name_or_id(options[:tenant])
89
+ if account.nil?
90
+ return 1, "Tenant not found by name: #{options[:tenant]}"
91
+ else
92
+ params['tenantId'] = account['id']
93
+ end
94
+ else
95
+ params['tenantId'] = options[:tenant]
96
+ end
97
+ end
76
98
  @networks_interface.setopts(options)
77
99
  if options[:dry_run]
78
100
  print_dry_run @networks_interface.dry.list(params)
@@ -109,6 +131,7 @@ class Morpheus::Cli::NetworksCommand
109
131
  type: network['type'] ? network['type']['name'] : '',
110
132
  group: network['group'] ? network['group']['name'] : 'Shared',
111
133
  cloud: network['zone'] ? network['zone']['name'] : '',
134
+ tenant: network['owner'] ? network['owner']['name'] : '',
112
135
  cidr: network['cidr'],
113
136
  pool: network['pool'] ? network['pool']['name'] : '',
114
137
  dhcp: network['dhcpServer'] ? 'Yes' : 'No',
@@ -128,6 +151,7 @@ class Morpheus::Cli::NetworksCommand
128
151
  type: "Subnet",
129
152
  group: network['group'] ? network['group']['name'] : 'Shared',
130
153
  cloud: network['zone'] ? network['zone']['name'] : '',
154
+ tenant: network['owner'] ? network['owner']['name'] : '',
131
155
  cidr: subnet['cidr'],
132
156
  pool: subnet['pool'] ? subnet['pool']['name'] : '',
133
157
  dhcp: subnet['dhcpServer'] ? 'Yes' : 'No',
@@ -139,9 +163,13 @@ class Morpheus::Cli::NetworksCommand
139
163
  end
140
164
  end
141
165
  end
142
- columns = [:id, :name, :labels, :type, :group, :cloud, :cidr, :pool, :dhcp, :subnets, :active, :visibility, :tenants]
166
+ columns = [:id, :name, :labels, :type, :group, :cloud, :tenant, :cidr, :pool, :dhcp, :subnets, :active, :visibility, :tenants]
143
167
  if options[:include_fields]
144
168
  columns = options[:include_fields]
169
+ else
170
+ if !options[:include_tenants] && !options[:tenant]
171
+ columns.delete(:tenant)
172
+ end
145
173
  end
146
174
  print cyan
147
175
  print as_pretty_table(rows, columns, options)
@@ -160,6 +188,9 @@ class Morpheus::Cli::NetworksCommand
160
188
  options = {}
161
189
  optparse = Morpheus::Cli::OptionParser.new do |opts|
162
190
  opts.banner = subcommand_usage("[network]")
191
+ opts.on('--include-tenants','--include-tenants', "Include sub tenant networks when finding network by name") do
192
+ options[:include_tenants] = true
193
+ end
163
194
  build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
164
195
  opts.footer = "Get details about a network." + "\n" +
165
196
  "[network] is required. This is the name or id of a network."
@@ -177,11 +208,11 @@ class Morpheus::Cli::NetworksCommand
177
208
  if args[0].to_s =~ /\A\d{1,}\Z/
178
209
  print_dry_run @networks_interface.dry.get(args[0].to_i)
179
210
  else
180
- print_dry_run @networks_interface.dry.list({name:args[0]})
211
+ print_dry_run @networks_interface.dry.list({name:args[0]}, options[:include_tenants])
181
212
  end
182
213
  return
183
214
  end
184
- network = find_network_by_name_or_id(args[0])
215
+ network = find_network_by_name_or_id(args[0], options[:include_tenants])
185
216
  return 1 if network.nil?
186
217
  json_response = {'network' => network} # skip redundant request
187
218
  # json_response = @networks_interface.get(network['id'])
@@ -206,6 +237,7 @@ class Morpheus::Cli::NetworksCommand
206
237
  "Type" => lambda {|it| it['type'] ? it['type']['name'] : '' },
207
238
  "Group" => lambda {|it| it['group'] ? it['group']['name'] : 'Shared' },
208
239
  "Cloud" => lambda {|it| it['zone'] ? it['zone']['name'] : '' },
240
+ "Tenant" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
209
241
  "IPv4 Enabled" => 'ipv4Enabled',
210
242
  "IPv6 Enabled" => 'ipv6Enabled',
211
243
  "CIDR" => 'cidr',
@@ -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
- json_response = networks_interface.list({name: name.to_s})
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}"
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "9.0.1"
4
+ VERSION = "9.0.2"
5
5
  end
6
6
  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.1
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-07-24 00:00:00.000000000 Z
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