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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +4 -0
- data/lib/morpheus/api/clusters_interface.rb +48 -0
- data/lib/morpheus/api/servers_interface.rb +8 -0
- data/lib/morpheus/api/virtual_switches_interface.rb +9 -0
- data/lib/morpheus/cli/commands/backups_command.rb +11 -0
- data/lib/morpheus/cli/commands/clusters.rb +503 -14
- data/lib/morpheus/cli/commands/hosts.rb +72 -1
- data/lib/morpheus/cli/commands/instances.rb +12 -1
- data/lib/morpheus/cli/commands/networks_command.rb +35 -3
- 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
- metadata +4 -2
|
@@ -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, "
|
|
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
|
-
|
|
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
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
|