morpheus-cli 8.0.4.1 → 8.0.6
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 +8 -0
- data/lib/morpheus/api/backup_results_interface.rb +4 -0
- data/lib/morpheus/api/clusters_interface.rb +8 -0
- data/lib/morpheus/api/network_floating_ips_interface.rb +3 -0
- data/lib/morpheus/api/server_devices_interface.rb +30 -0
- data/lib/morpheus/api/storage_datastores_interface.rb +44 -0
- data/lib/morpheus/cli/commands/backups_command.rb +16 -2
- data/lib/morpheus/cli/commands/clients_command.rb +14 -7
- data/lib/morpheus/cli/commands/cloud_datastores_command.rb +113 -1
- data/lib/morpheus/cli/commands/clouds.rb +0 -1
- data/lib/morpheus/cli/commands/clouds_types.rb +0 -1
- data/lib/morpheus/cli/commands/clusters.rb +36 -1
- data/lib/morpheus/cli/commands/containers_command.rb +21 -19
- data/lib/morpheus/cli/commands/hosts.rb +25 -4
- data/lib/morpheus/cli/commands/license.rb +12 -2
- data/lib/morpheus/cli/commands/network_domains_command.rb +1 -1
- data/lib/morpheus/cli/commands/network_floating_ips.rb +39 -1
- data/lib/morpheus/cli/commands/network_routers_command.rb +14 -1
- data/lib/morpheus/cli/commands/server_devices_command.rb +207 -0
- data/lib/morpheus/cli/commands/storage_datastores.rb +457 -0
- data/lib/morpheus/cli/commands/user_settings_command.rb +22 -6
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +1 -0
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +16 -3
- data/lib/morpheus/cli/option_types.rb +5 -5
- data/lib/morpheus/cli/version.rb +1 -1
- data/test/api/clients_interface_test.rb +23 -0
- data/test/cli/clients_test.rb +45 -0
- metadata +10 -2
@@ -840,7 +840,7 @@ class Morpheus::Cli::NetworkDomainsCommand
|
|
840
840
|
print_red_alert "#{network_domains.size} network domains found by name #{name}"
|
841
841
|
# print_networks_table(networks, {color: red})
|
842
842
|
rows = network_domains.collect do |network_domain|
|
843
|
-
{id:
|
843
|
+
{id: network_domain['id'], name: network_domain['name']}
|
844
844
|
end
|
845
845
|
puts as_pretty_table(rows, [:id, :name], {color:red})
|
846
846
|
return nil
|
@@ -6,7 +6,7 @@ class Morpheus::Cli::NetworkFloatingIps
|
|
6
6
|
|
7
7
|
set_command_name :'network-floating-ips'
|
8
8
|
set_command_description "View and manage network floating IPs."
|
9
|
-
register_subcommands :list, :get, :release
|
9
|
+
register_subcommands :list, :get, :release, :allocate
|
10
10
|
|
11
11
|
# RestCommand settings
|
12
12
|
register_interfaces :network_floating_ips
|
@@ -49,6 +49,44 @@ EOT
|
|
49
49
|
return 0, nil
|
50
50
|
end
|
51
51
|
|
52
|
+
def allocate(args)
|
53
|
+
options = {}
|
54
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
55
|
+
opts.banner = subcommand_usage()
|
56
|
+
build_standard_add_options(opts, options)
|
57
|
+
opts.footer = <<-EOT
|
58
|
+
Allocate a new #{rest_label.downcase}.
|
59
|
+
Only the following cloud types support this command: OpenStack, Huawei and OpenTelekom
|
60
|
+
EOT
|
61
|
+
end
|
62
|
+
optparse.parse!(args)
|
63
|
+
verify_args!(args:args, optparse:optparse, count:0)
|
64
|
+
connect(options)
|
65
|
+
payload = {}
|
66
|
+
if options[:payload]
|
67
|
+
payload = options[:payload]
|
68
|
+
payload.deep_merge!(parse_passed_options(options))
|
69
|
+
else
|
70
|
+
payload.deep_merge!(parse_passed_options(options))
|
71
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'networkServerId', 'fieldLabel' => 'Network Service', 'type' => 'select', 'optionSource' => 'floatingIpNetworkServers', 'required' => true, 'description' => 'Choose a network service.'}], options[:options], @api_client, {})
|
72
|
+
payload[:networkServerId] = v_prompt['networkServerId']
|
73
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'floatingIpPoolId', 'fieldLabel' => 'Network Floating Ip Pool', 'type' => 'select', 'optionSource' => 'floatingIpPools', 'required' => true, 'description' => 'Choose a network service.'}], options[:options], @api_client, {networkServerId: v_prompt['networkServerId']})
|
74
|
+
payload[:floatingIpPoolId] = v_prompt['floatingIpPoolId']
|
75
|
+
end
|
76
|
+
|
77
|
+
rest_interface.setopts(options)
|
78
|
+
if options[:dry_run]
|
79
|
+
print_dry_run rest_interface.dry.allocate(payload)
|
80
|
+
return 0, nil
|
81
|
+
end
|
82
|
+
json_response = rest_interface.allocate(payload)
|
83
|
+
render_response(json_response, options) do
|
84
|
+
print_green_success "Allocating #{rest_label.downcase} with ipAddress: #{json_response['networkFloatingIp']['ipAddress'] }"
|
85
|
+
id = json_response['networkFloatingIp']['id']
|
86
|
+
_get(id.to_s, {}, options )
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
52
90
|
protected
|
53
91
|
|
54
92
|
def build_list_options(opts, options, params)
|
@@ -328,6 +328,8 @@ class Morpheus::Cli::NetworkRoutersCommand
|
|
328
328
|
cloud = {'id' => cloud_id}
|
329
329
|
end
|
330
330
|
router['zone'] = params['zone'] = {'id' => cloud['id']}
|
331
|
+
# add router type to be used for option prompts
|
332
|
+
params['router']['zone'] = {'id' => cloud['id']}
|
331
333
|
end
|
332
334
|
|
333
335
|
# prompt for enabled
|
@@ -337,7 +339,18 @@ class Morpheus::Cli::NetworkRoutersCommand
|
|
337
339
|
|
338
340
|
# prompt options
|
339
341
|
option_opts = options[:options].deep_merge!({'config' => options[:options].clone})
|
340
|
-
|
342
|
+
# option types are mixing context router and networkRouter, so we need to copy these to avoid prompt for router.name if passed in
|
343
|
+
if option_opts['name'] || option_opts['description']
|
344
|
+
option_opts['router'] ||= {}
|
345
|
+
option_opts['router']['name'] = option_opts['name'] if option_opts['name']
|
346
|
+
option_opts['router']['description'] = option_opts['description'] if option_opts['description']
|
347
|
+
end
|
348
|
+
option_result = Morpheus::Cli::OptionTypes.prompt(option_types, option_opts.merge({:context_map => {'networkRouter' => 'router'}}), @api_client, params)
|
349
|
+
# option types are mixing context router and networkRouter, so we need to clean up the payload
|
350
|
+
router_params = option_result.delete('router')
|
351
|
+
if router_params.is_a?(Hash)
|
352
|
+
option_result = router_params.deep_merge(option_result)
|
353
|
+
end
|
341
354
|
payload = {'networkRouter' => router.deep_merge(option_result)}
|
342
355
|
payload['networkRouter']['config'] = option_result['config'] if option_result['config']
|
343
356
|
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
|
3
|
+
class Morpheus::Cli::ServerDevicesCommand
|
4
|
+
include Morpheus::Cli::CliCommand
|
5
|
+
include Morpheus::Cli::ProvisioningHelper
|
6
|
+
# include Morpheus::Cli::OptionSourceHelper
|
7
|
+
|
8
|
+
set_command_hidden # hide and prefer `hosts list-devices, get-device, assign-device. for now
|
9
|
+
|
10
|
+
set_command_name :'host-devices'
|
11
|
+
|
12
|
+
register_subcommands :list, :assign, :attach, :detach
|
13
|
+
|
14
|
+
def connect(opts)
|
15
|
+
@api_client = establish_remote_appliance_connection(opts)
|
16
|
+
@servers_interface = @api_client.servers
|
17
|
+
@server_devices_interface = @api_client.server_devices
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle(args)
|
21
|
+
handle_subcommand(args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def list(args)
|
25
|
+
options = {}
|
26
|
+
params = {}
|
27
|
+
ref_ids = []
|
28
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
29
|
+
opts.banner = "Usage: #{prog_name} hosts [host] list-devices"
|
30
|
+
build_standard_list_options(opts, options)
|
31
|
+
opts.footer = <<-EOT
|
32
|
+
List host devices.
|
33
|
+
[host] is required. This is the id of the host.
|
34
|
+
EOT
|
35
|
+
end
|
36
|
+
optparse.parse!(args)
|
37
|
+
connect(options)
|
38
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
39
|
+
server_id = args[0] =~ /\A\d{1,}\Z/ ? args[0].to_i : find_server_by_name(args[0])['id']
|
40
|
+
@server_devices_interface.setopts(options)
|
41
|
+
if options[:dry_run]
|
42
|
+
print_dry_run @server_devices_interface.dry.list(server_id, params)
|
43
|
+
return
|
44
|
+
end
|
45
|
+
json_response = @server_devices_interface.list(server_id, params)
|
46
|
+
render_response(json_response, options, 'devices') do
|
47
|
+
server_devices = json_response['devices']
|
48
|
+
print_h1 "Host Devices: #{server_id}", parse_list_subtitles(options), options
|
49
|
+
if server_devices.empty?
|
50
|
+
print cyan,"No host devices found.",reset,"\n"
|
51
|
+
else
|
52
|
+
print as_pretty_table(server_devices, server_device_list_column_definitions.upcase_keys!, options)
|
53
|
+
print_results_pagination(json_response)
|
54
|
+
end
|
55
|
+
print reset,"\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def assign(args)
|
61
|
+
options = {}
|
62
|
+
params = {}
|
63
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
64
|
+
opts.banner = "Usage: #{prog_name} hosts assign [host] [device] [target]"
|
65
|
+
build_standard_update_options(opts, options)
|
66
|
+
opts.footer = <<-EOT
|
67
|
+
Assign a host device to a target server.
|
68
|
+
[host] is required. This is the id of the host.
|
69
|
+
[device] is required. This is the id of the device.
|
70
|
+
[target] is required. This is the id of the target server.
|
71
|
+
EOT
|
72
|
+
end
|
73
|
+
optparse.parse!(args)
|
74
|
+
verify_args!(args:args, optparse:optparse, min:1, max:3)
|
75
|
+
connect(options)
|
76
|
+
server_id = args[0] =~ /\A\d{1,}\Z/ ? args[0].to_i : find_server_by_name(args[0])['id']
|
77
|
+
device_id = args[1] ? args[1].to_i : nil
|
78
|
+
if device_id.nil?
|
79
|
+
avail_devices = @server_devices_interface.list(server_id)['devices'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
80
|
+
device_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'deviceId', 'fieldLabel' => 'Device', 'type' => 'select', 'selectOptions' => avail_devices, 'required' => true}], options[:options], @api_client)['deviceId']
|
81
|
+
end
|
82
|
+
target_server_id = args[2] ? args[2].to_i : nil
|
83
|
+
parse_payload(options) do |payload|
|
84
|
+
if target_server_id.nil?
|
85
|
+
# avail_servers = @servers_interface.list({max:10000, 'parentId' => server_id})['servers'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
86
|
+
# target_server_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'targetServerId', 'fieldLabel' => 'Target Server', 'type' => 'select', 'selectOptions' => avail_servers, 'required' => true}], options[:options], @api_client)['targetServerId']
|
87
|
+
target_server_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'targetServerId', 'fieldLabel' => 'Target Server', 'type' => 'select', 'optionSource' => 'hostVms', 'required' => true}], options[:options], @api_client, {'parentId' => server_id})['targetServerId']
|
88
|
+
end
|
89
|
+
payload['targetServerId'] = target_server_id
|
90
|
+
end
|
91
|
+
execute_api(@server_devices_interface, :assign, [server_id, device_id], options) do |json_response|
|
92
|
+
print_green_success "Assigned host device to target server"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def attach(args)
|
97
|
+
options = {}
|
98
|
+
params = {}
|
99
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
100
|
+
opts.banner = "Usage: #{prog_name} hosts attach [host] [device]"
|
101
|
+
build_standard_update_options(opts, options)
|
102
|
+
opts.footer = <<-EOT
|
103
|
+
Attach a host device.
|
104
|
+
[host] is required. This is the id of the host.
|
105
|
+
[device] is required. This is the id of the device.
|
106
|
+
EOT
|
107
|
+
end
|
108
|
+
optparse.parse!(args)
|
109
|
+
verify_args!(args:args, optparse:optparse, min:1, max:3)
|
110
|
+
connect(options)
|
111
|
+
server_id = args[0] =~ /\A\d{1,}\Z/ ? args[0].to_i : find_server_by_name(args[0])['id']
|
112
|
+
device_id = args[1] ? args[1].to_i : nil
|
113
|
+
if device_id.nil?
|
114
|
+
avail_devices = @server_devices_interface.list(server_id)['devices'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
115
|
+
device_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'deviceId', 'fieldLabel' => 'Device', 'type' => 'select', 'selectOptions' => avail_devices, 'required' => true}], options[:options], @api_client)['deviceId']
|
116
|
+
end
|
117
|
+
parse_payload(options) do |payload|
|
118
|
+
|
119
|
+
end
|
120
|
+
execute_api(@server_devices_interface, :attach, [server_id, device_id], options) do |json_response|
|
121
|
+
print_green_success "Attached host device"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def detach(args)
|
126
|
+
options = {}
|
127
|
+
params = {}
|
128
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
129
|
+
opts.banner = "Usage: #{prog_name} hosts detach [host] [device]"
|
130
|
+
build_standard_update_options(opts, options)
|
131
|
+
opts.footer = <<-EOT
|
132
|
+
Detach a host device.
|
133
|
+
[host] is required. This is the id of the host.
|
134
|
+
[device] is required. This is the id of the device.
|
135
|
+
EOT
|
136
|
+
end
|
137
|
+
optparse.parse!(args)
|
138
|
+
verify_args!(args:args, optparse:optparse, min:1, max:3)
|
139
|
+
connect(options)
|
140
|
+
server_id = args[0] =~ /\A\d{1,}\Z/ ? args[0].to_i : find_server_by_name(args[0])['id']
|
141
|
+
device_id = args[1] ? args[1].to_i : nil
|
142
|
+
if device_id.nil?
|
143
|
+
avail_devices = @server_devices_interface.list(server_id)['devices'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
144
|
+
device_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'deviceId', 'fieldLabel' => 'Device', 'type' => 'select', 'selectOptions' => avail_devices, 'required' => true}], options[:options], @api_client)['deviceId']
|
145
|
+
end
|
146
|
+
parse_payload(options) do |payload|
|
147
|
+
|
148
|
+
end
|
149
|
+
execute_api(@server_devices_interface, :detach, [server_id, device_id], options) do |json_response|
|
150
|
+
print_green_success "Detached host device"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
# helper methods
|
157
|
+
|
158
|
+
def server_device_list_column_definitions()
|
159
|
+
# {
|
160
|
+
# "ID" => 'id',
|
161
|
+
# "Name" => 'name',
|
162
|
+
# "Ref" => lambda {|it| "#{it['refType']} #{it['refId']}" },
|
163
|
+
# "Type" => lambda {|it| it['type']['name'] rescue '' },
|
164
|
+
# "Status" => lambda {|it| format_server_device_status(it) },
|
165
|
+
# "External ID" => 'externalId',
|
166
|
+
# "Domain ID" => 'domainId',
|
167
|
+
# "Bus" => 'bus',
|
168
|
+
# "Slot" => 'slot',
|
169
|
+
# "Device" => 'device',
|
170
|
+
# "Vendor ID" => 'vendorId',
|
171
|
+
# "Product ID" => 'productId',
|
172
|
+
# "Function ID" => 'functionId',
|
173
|
+
# "Unique ID" => 'uniqueId',
|
174
|
+
# "IOMMU Group" => 'iommuGroup',
|
175
|
+
# "IOMMU Device Count" => 'iommuDeviceCount',
|
176
|
+
# }
|
177
|
+
{
|
178
|
+
"ID" => 'id',
|
179
|
+
"Name" => 'name',
|
180
|
+
"Type" => lambda {|it| it['type']['name'] rescue '' },
|
181
|
+
"Bus" => 'bus',
|
182
|
+
"Slot" => 'slot',
|
183
|
+
"Status" => lambda {|it| format_server_device_status(it) },
|
184
|
+
"Assignee" => lambda {|it| "#{it['refType']} #{it['refId']}" },
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
def server_device_column_definitions()
|
189
|
+
server_device_list_column_definitions()
|
190
|
+
end
|
191
|
+
|
192
|
+
def format_server_device_status(server_device, return_color=cyan)
|
193
|
+
out = ""
|
194
|
+
status_string = server_device['status'].to_s.upcase
|
195
|
+
if status_string == 'ATTACHED' # || status_string == 'ASSIGNED'
|
196
|
+
out << "#{green}#{status_string.upcase}#{return_color}"
|
197
|
+
elsif status_string == "PENDING"
|
198
|
+
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
199
|
+
elsif status_string
|
200
|
+
out << "#{cyan}#{status_string.upcase}#{return_color}"
|
201
|
+
else
|
202
|
+
out << ""
|
203
|
+
end
|
204
|
+
out
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|