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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +8 -0
  4. data/lib/morpheus/api/backup_results_interface.rb +4 -0
  5. data/lib/morpheus/api/clusters_interface.rb +8 -0
  6. data/lib/morpheus/api/network_floating_ips_interface.rb +3 -0
  7. data/lib/morpheus/api/server_devices_interface.rb +30 -0
  8. data/lib/morpheus/api/storage_datastores_interface.rb +44 -0
  9. data/lib/morpheus/cli/commands/backups_command.rb +16 -2
  10. data/lib/morpheus/cli/commands/clients_command.rb +14 -7
  11. data/lib/morpheus/cli/commands/cloud_datastores_command.rb +113 -1
  12. data/lib/morpheus/cli/commands/clouds.rb +0 -1
  13. data/lib/morpheus/cli/commands/clouds_types.rb +0 -1
  14. data/lib/morpheus/cli/commands/clusters.rb +36 -1
  15. data/lib/morpheus/cli/commands/containers_command.rb +21 -19
  16. data/lib/morpheus/cli/commands/hosts.rb +25 -4
  17. data/lib/morpheus/cli/commands/license.rb +12 -2
  18. data/lib/morpheus/cli/commands/network_domains_command.rb +1 -1
  19. data/lib/morpheus/cli/commands/network_floating_ips.rb +39 -1
  20. data/lib/morpheus/cli/commands/network_routers_command.rb +14 -1
  21. data/lib/morpheus/cli/commands/server_devices_command.rb +207 -0
  22. data/lib/morpheus/cli/commands/storage_datastores.rb +457 -0
  23. data/lib/morpheus/cli/commands/user_settings_command.rb +22 -6
  24. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +1 -0
  25. data/lib/morpheus/cli/mixins/provisioning_helper.rb +16 -3
  26. data/lib/morpheus/cli/option_types.rb +5 -5
  27. data/lib/morpheus/cli/version.rb +1 -1
  28. data/test/api/clients_interface_test.rb +23 -0
  29. data/test/cli/clients_test.rb +45 -0
  30. 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: it['id'], name: it['name']}
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
- option_result = Morpheus::Cli::OptionTypes.prompt(option_types, option_opts.merge({:context_map => {'networkRouter' => ''}}), @api_client, params)
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