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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bd49f7ba0c5284049c949b7eb9abab30586335cf7994071467ac2e40e5052f8
|
4
|
+
data.tar.gz: 8042138fa3aa60542f6a042a035172f15aac37483fd3fea25fd11ed550e1ba29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24dd7b12d6f4a3884844af6ad53736cfecf3c28102aae2f2b8a710bac97433955aae2d7771f367ae8241f721a701c5a8d375a95fe065af5162280ebbe737d342
|
7
|
+
data.tar.gz: 1dca804ca56f509dcf13d9a35312de4429b0163e74f4140952fa06d15b8928f8ebe3132537b77ae1939307a2fcb69aa9d9ea52e2293753a042fdf5a6cf8da2f2
|
data/Dockerfile
CHANGED
@@ -412,6 +412,10 @@ class Morpheus::APIClient
|
|
412
412
|
Morpheus::ServersInterface.new(common_interface_options).setopts(@options)
|
413
413
|
end
|
414
414
|
|
415
|
+
def server_devices
|
416
|
+
Morpheus::ServerDevicesInterface.new(common_interface_options).setopts(@options)
|
417
|
+
end
|
418
|
+
|
415
419
|
def instances
|
416
420
|
Morpheus::InstancesInterface.new(common_interface_options).setopts(@options)
|
417
421
|
end
|
@@ -742,6 +746,10 @@ class Morpheus::APIClient
|
|
742
746
|
Morpheus::StorageVolumeTypesInterface.new(common_interface_options).setopts(@options)
|
743
747
|
end
|
744
748
|
|
749
|
+
def storage_datastores
|
750
|
+
Morpheus::StorageDatastoresInterface.new(common_interface_options).setopts(@options)
|
751
|
+
end
|
752
|
+
|
745
753
|
def library_instance_types
|
746
754
|
Morpheus::LibraryInstanceTypesInterface.new(common_interface_options).setopts(@options)
|
747
755
|
end
|
@@ -25,4 +25,8 @@ class Morpheus::BackupResultsInterface < Morpheus::APIClient
|
|
25
25
|
execute(method: :delete, url: "#{base_path}/#{CGI::escape(id.to_s)}", params: params, headers: headers)
|
26
26
|
end
|
27
27
|
|
28
|
+
def create_options(id, payload={}, params={}, headers={})
|
29
|
+
validate_id!(id)
|
30
|
+
execute(method: :post, url: "#{base_path}/#{CGI::escape(id.to_s)}/create-restore", params: params, payload: payload, headers: headers)
|
31
|
+
end
|
28
32
|
end
|
@@ -345,4 +345,12 @@ class Morpheus::ClustersInterface < Morpheus::APIClient
|
|
345
345
|
execute(method: :get, url: url, headers: headers)
|
346
346
|
end
|
347
347
|
|
348
|
+
def load_balancer_port(params={})
|
349
|
+
url = "#{@base_url}/api/clusters/load-balancer-port"
|
350
|
+
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
351
|
+
headers[:params].merge!(params)
|
352
|
+
|
353
|
+
execute(method: :get, url: url, headers: headers)
|
354
|
+
end
|
355
|
+
|
348
356
|
end
|
@@ -34,4 +34,7 @@ class Morpheus::NetworkFloatingIpsInterface < Morpheus::APIClient
|
|
34
34
|
execute(method: :put, url: "#{base_path}/#{CGI::escape(id.to_s)}/release", params: params, payload: payload, headers: headers)
|
35
35
|
end
|
36
36
|
|
37
|
+
def allocate(payload={}, params={}, headers={})
|
38
|
+
execute(method: :post, url: "#{base_path}/allocate", params: params, payload: payload, headers: headers)
|
39
|
+
end
|
37
40
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'morpheus/api/api_client'
|
2
|
+
|
3
|
+
class Morpheus::ServerDevicesInterface < Morpheus::APIClient
|
4
|
+
|
5
|
+
def base_path(server_id)
|
6
|
+
"#{@base_url}/api/servers/#{server_id}"
|
7
|
+
end
|
8
|
+
|
9
|
+
# def get(server_id, id, params={})
|
10
|
+
# raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
11
|
+
# execute({method: :get, url: "#{base_path(server_id)}/devices/#{id}", params: params})
|
12
|
+
# end
|
13
|
+
|
14
|
+
def list(server_id, params={})
|
15
|
+
execute({method: :get, url: "#{base_path(server_id)}/devices", params: params})
|
16
|
+
end
|
17
|
+
|
18
|
+
def assign(server_id, id, payload)
|
19
|
+
execute({method: :put, url: "#{base_path(server_id)}/devices/#{id}/assign", payload: payload.to_json})
|
20
|
+
end
|
21
|
+
|
22
|
+
def attach(server_id, id, payload)
|
23
|
+
execute({method: :put, url: "#{base_path(server_id)}/devices/#{id}/attach", payload: payload.to_json})
|
24
|
+
end
|
25
|
+
|
26
|
+
def detach(server_id, id, payload)
|
27
|
+
execute({method: :put, url: "#{base_path(server_id)}/devices/#{id}/detach", payload: payload.to_json})
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'morpheus/api/api_client'
|
2
|
+
|
3
|
+
class Morpheus::StorageDatastoresInterface < Morpheus::APIClient
|
4
|
+
|
5
|
+
def base_path
|
6
|
+
"#{@base_url}/api/data-stores"
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(id, params={})
|
10
|
+
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
11
|
+
url = "#{@base_url}/api/data-stores/#{id}"
|
12
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
13
|
+
opts = {method: :get, url: url, headers: headers}
|
14
|
+
execute(opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def list(params={})
|
18
|
+
url = base_path
|
19
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
20
|
+
opts = {method: :get, url: url, headers: headers}
|
21
|
+
execute(opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_type_options(datastore_type)
|
25
|
+
url = "/api/data-stores/#{datastore_type}/option-types"
|
26
|
+
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
27
|
+
opts = {method: :get, url: url, headers: headers}
|
28
|
+
execute(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def create(options)
|
32
|
+
url = "#{@base_url}/api/data-stores"
|
33
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
34
|
+
payload = options
|
35
|
+
execute(method: :post, url: url, headers: headers, payload: payload.to_json)
|
36
|
+
end
|
37
|
+
|
38
|
+
def update(id, payload)
|
39
|
+
url = "#{@base_url}/api/data-stores/#{id}"
|
40
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
41
|
+
opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
|
42
|
+
execute(opts)
|
43
|
+
end
|
44
|
+
end
|
@@ -373,10 +373,21 @@ EOT
|
|
373
373
|
# could actually fetch the instance.., only need name and id right now though.
|
374
374
|
raise_command_error "Backup instance not found" if instance.nil?
|
375
375
|
params['restoreInstance'] = prompt_value({'fieldName' => 'restoreInstance', 'fieldLabel' => 'Restore Instance', 'type' => 'select', 'selectOptions' => [{'name' => 'Current Instance', 'value' => 'existing'}, {'name' => 'New Instance', 'value' => 'new'}], 'defaultValue' => 'existing', 'required' => true, 'description' => 'Restore the current instance or a new instance?'}, options)
|
376
|
+
restore_options = @backup_results_interface.create_options(backup_result['id'])
|
376
377
|
if params['restoreInstance'] == 'new'
|
377
378
|
# new instance
|
378
|
-
|
379
|
-
|
379
|
+
if restore_options['vmRestore']
|
380
|
+
group_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group', 'selectOptions' => get_available_groups(), 'required' => true, 'description' => 'Select Group.'}],options[:options],@api_client,{'serverTypeId' => restore_options['servierTypeId']})['group']
|
381
|
+
name = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Instance Name', 'defaultValue' => "#{backup['name']}-restore"}], options[:options], @api_client)['name']
|
382
|
+
params['groupId'] = group_id
|
383
|
+
params['name'] = name
|
384
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(restore_options['restoreNewOptionTypes'], options[:options], @api_client, {'zoneId' => backup_result['zoneId'], 'siteId' => group_id})
|
385
|
+
v_prompt.deep_compact!.booleanize! # remove empty values and convert checkbox "on" and "off" to true and false
|
386
|
+
params.deep_merge!(v_prompt)
|
387
|
+
else
|
388
|
+
config_map = prompt_restore_instance_config(options)
|
389
|
+
params['instanceConfig'] = config_map
|
390
|
+
end
|
380
391
|
else
|
381
392
|
# existing instance
|
382
393
|
# confirm the instance
|
@@ -393,6 +404,9 @@ EOT
|
|
393
404
|
print_red_alert "The value '#{instance_id}' does not match the existing instance #{instance['name']} [#{instance['id'] rescue ''}]. Please try again."
|
394
405
|
end
|
395
406
|
end
|
407
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(restore_options['restoreExistingOptionTypes'], options[:options], @api_client, {'zoneId' => backup_result['zoneId'], 'siteId' => group_id})
|
408
|
+
v_prompt.deep_compact!.booleanize! # remove empty values and convert checkbox "on" and "off" to true and false
|
409
|
+
params.deep_merge!(v_prompt)
|
396
410
|
end
|
397
411
|
elsif backup['locationType'] == 'server'
|
398
412
|
# prompt for server type backup restore
|
@@ -102,9 +102,10 @@ class Morpheus::Cli::ClientsCommand
|
|
102
102
|
"ID" => 'id',
|
103
103
|
"Client ID" => 'clientId',
|
104
104
|
"Access Token Validity Seconds" => 'accessTokenValiditySeconds',
|
105
|
-
"Refresh Token Validity Seconds" => 'refreshTokenValiditySeconds'
|
105
|
+
"Refresh Token Validity Seconds" => 'refreshTokenValiditySeconds',
|
106
|
+
# "Scopes" => lambda {|client| client['scopes'].join(", ")},
|
107
|
+
"Redirect URI" => lambda {|client| client['redirectUris'].join(", ")}
|
106
108
|
}
|
107
|
-
|
108
109
|
print_description_list(client_columns, client)
|
109
110
|
print reset,"\n"
|
110
111
|
|
@@ -122,7 +123,7 @@ class Morpheus::Cli::ClientsCommand
|
|
122
123
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
123
124
|
opts.banner = subcommand_usage("[clientId] [options]")
|
124
125
|
build_option_type_options(opts, options, add_client_option_types)
|
125
|
-
|
126
|
+
build_standard_add_options(opts, options)
|
126
127
|
opts.footer = "Add New Oauth Client Record."
|
127
128
|
end
|
128
129
|
optparse.parse!(args)
|
@@ -150,6 +151,9 @@ class Morpheus::Cli::ClientsCommand
|
|
150
151
|
payload.deep_merge!({'client' => passed_options}) unless passed_options.empty?
|
151
152
|
# prompt for options
|
152
153
|
params = Morpheus::Cli::OptionTypes.prompt(add_client_option_types, options[:options], @api_client, options[:params])
|
154
|
+
if params['redirectUris'] && params['redirectUris'].is_a?(String)
|
155
|
+
params['redirectUris'] = params['redirectUris'].split(',').collect {|it| it.strip}.reject {|it| it.empty?}
|
156
|
+
end
|
153
157
|
payload.deep_merge!({'client' => params}) unless params.empty?
|
154
158
|
end
|
155
159
|
|
@@ -179,7 +183,7 @@ class Morpheus::Cli::ClientsCommand
|
|
179
183
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
180
184
|
opts.banner = subcommand_usage("[clientId] [options]")
|
181
185
|
build_option_type_options(opts, options, client_option_types)
|
182
|
-
|
186
|
+
build_standard_update_options(opts, options)
|
183
187
|
opts.footer = "Update Oauth Client Record."
|
184
188
|
end
|
185
189
|
optparse.parse!(args)
|
@@ -206,11 +210,13 @@ class Morpheus::Cli::ClientsCommand
|
|
206
210
|
}
|
207
211
|
}
|
208
212
|
# allow arbitrary -O options
|
209
|
-
payload.deep_merge!({'
|
213
|
+
payload.deep_merge!({'client' => passed_options}) unless passed_options.empty?
|
210
214
|
# prompt for options
|
211
215
|
#params = Morpheus::Cli::OptionTypes.prompt(update_wiki_page_option_types, options[:options], @api_client, options[:params])
|
212
216
|
params = passed_options
|
213
|
-
|
217
|
+
if params['redirectUris'] && params['redirectUris'].is_a?(String)
|
218
|
+
params['redirectUris'] = params['redirectUris'].split(',').collect {|it| it.strip}.reject {|it| it.empty?}
|
219
|
+
end
|
214
220
|
if params.empty?
|
215
221
|
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
216
222
|
end
|
@@ -332,7 +338,8 @@ class Morpheus::Cli::ClientsCommand
|
|
332
338
|
{'fieldName' => 'clientId', 'fieldLabel' => 'Client Id', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
333
339
|
{'fieldName' => 'clientSecret', 'fieldLabel' => 'Client Secret', 'type' => 'text', 'displayOrder' => 2},
|
334
340
|
{'fieldName' => 'accessTokenValiditySeconds', 'fieldLabel' => 'Access Token Validity Length (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'displayOrder' => 3},
|
335
|
-
{'fieldName' => 'refreshTokenValiditySeconds', 'fieldLabel' => 'Refresh Token Validity Length (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'displayOrder' => 4}
|
341
|
+
{'fieldName' => 'refreshTokenValiditySeconds', 'fieldLabel' => 'Refresh Token Validity Length (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'displayOrder' => 4},
|
342
|
+
{'fieldName' => 'redirectUris', 'fieldLabel' => 'Redirect URI', 'type' => 'text', 'displayOrder' => 5}
|
336
343
|
]
|
337
344
|
end
|
338
345
|
end
|
@@ -7,7 +7,7 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
7
7
|
# set_command_name :'cloud-datastores'
|
8
8
|
set_command_name :'datastores'
|
9
9
|
|
10
|
-
register_subcommands :list, :get, :update
|
10
|
+
register_subcommands :list, :get, :update, :add
|
11
11
|
|
12
12
|
def initialize()
|
13
13
|
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
@@ -18,6 +18,7 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
18
18
|
@cloud_datastores_interface = @api_client.cloud_datastores
|
19
19
|
@clouds_interface = @api_client.clouds
|
20
20
|
@options_interface = @api_client.options
|
21
|
+
@storage_datastore_interface = @api_client.storage_datastores
|
21
22
|
end
|
22
23
|
|
23
24
|
def handle(args)
|
@@ -379,6 +380,114 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
379
380
|
end
|
380
381
|
end
|
381
382
|
|
383
|
+
def add(args)
|
384
|
+
options = {}
|
385
|
+
params = {}
|
386
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
387
|
+
opts.banner = subcommand_usage("[name] [options]")
|
388
|
+
build_common_options(opts, options, [:options, :payload, :json, :yaml, :dry_run, :quiet])
|
389
|
+
opts.on( '-n', '--name NAME', "Name" ) do |val|
|
390
|
+
options['name'] = val
|
391
|
+
end
|
392
|
+
opts.on( '-t', '--type DATASTORE_TYPE', "Datastore Type" ) do |val|
|
393
|
+
options['datastoreType'] = val
|
394
|
+
end
|
395
|
+
opts.on( '-c', '--cloud DATASTORE_CLOUD', "Datastore Cloud" ) do |val|
|
396
|
+
options['cloud'] = val
|
397
|
+
end
|
398
|
+
opts.footer = "Create a new Datastore.\n" +
|
399
|
+
"[name] is required. This is the name of the new datastore. It may also be passed as --name or inside your config."
|
400
|
+
end
|
401
|
+
optparse.parse!(args)
|
402
|
+
if args.count > 1
|
403
|
+
print_error Morpheus::Terminal.angry_prompt
|
404
|
+
puts_error "#{command_name} add expects 0-1 arguments and received #{args.count}: #{args}\n#{optparse}"
|
405
|
+
return 1
|
406
|
+
end
|
407
|
+
# allow name as first argument
|
408
|
+
if args[0] # && !options[:name]
|
409
|
+
options[:name] = args[0]
|
410
|
+
end
|
411
|
+
connect(options)
|
412
|
+
begin
|
413
|
+
options[:options] ||= {}
|
414
|
+
passed_options = (options[:options] || {}).reject {|k,v| k.is_a?(Symbol) }
|
415
|
+
payload = {}
|
416
|
+
if options[:payload]
|
417
|
+
# payload is from parsed json|yaml files or arguments.
|
418
|
+
payload = options[:payload]
|
419
|
+
# merge -O options
|
420
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
421
|
+
# support some options on top of --payload
|
422
|
+
[:name, :description, :environment].each do |k|
|
423
|
+
if options.key?(k)
|
424
|
+
payload[k.to_s] = options[k]
|
425
|
+
end
|
426
|
+
end
|
427
|
+
else
|
428
|
+
# prompt for payload
|
429
|
+
payload = {}
|
430
|
+
# merge -O options
|
431
|
+
payload.deep_merge!(passed_options) unless passed_options.empty?
|
432
|
+
|
433
|
+
# Name
|
434
|
+
if passed_options['name']
|
435
|
+
payload['name'] = passed_options['name']
|
436
|
+
else
|
437
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this archive bucket.'}], options, @api_client)
|
438
|
+
payload['name'] = v_prompt['name']
|
439
|
+
end
|
440
|
+
|
441
|
+
#Datastore Type
|
442
|
+
if passed_options['datastoreType']
|
443
|
+
payload['datastoreType'] = passed_options['datastoreType']
|
444
|
+
else
|
445
|
+
payload['datastoreType'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'datastoreType', 'fieldLabel' => 'Type', 'type' => 'select', 'required' => true, 'optionSource' => 'datastoreTypes'}], options[:options], @api_client)['datastoreType']
|
446
|
+
end
|
447
|
+
|
448
|
+
if passed_options['cloud']
|
449
|
+
zone = passed_options['cloud']
|
450
|
+
else
|
451
|
+
zone = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'zone', 'fieldLabel' => 'Cloud', 'type' => 'select', 'required' => true, 'optionSource' => 'cloudsForDatastores'}], options[:options], @api_client)['zone']
|
452
|
+
end
|
453
|
+
|
454
|
+
if zone
|
455
|
+
payload['refType'] = 'ComputeZone'
|
456
|
+
payload['refId'] = zone
|
457
|
+
end
|
458
|
+
|
459
|
+
option_types = load_option_types_for_datastore_type(payload['datastoreType'])
|
460
|
+
|
461
|
+
values = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client)
|
462
|
+
if values['domain']
|
463
|
+
payload.merge!(values['domain']) if values['domain'].is_a?(Hash)
|
464
|
+
end
|
465
|
+
if values['config']
|
466
|
+
payload['config'] = {}
|
467
|
+
payload['config'].merge!(values['config']) if values['config'].is_a?(Hash)
|
468
|
+
end
|
469
|
+
|
470
|
+
@storage_datastore_interface.setopts(options)
|
471
|
+
if options[:dry_run]
|
472
|
+
print_dry_run @storage_datastore_interface.dry.create({'datastore' => payload})
|
473
|
+
return
|
474
|
+
end
|
475
|
+
json_response = @storage_datastore_interface.create({'datastore' => payload})
|
476
|
+
datastore = json_response['datastore']
|
477
|
+
if options[:json]
|
478
|
+
print JSON.pretty_generate(json_response),"\n"
|
479
|
+
elsif !options[:quiet]
|
480
|
+
datastore = json_response['datastore']
|
481
|
+
print_green_success "Datastore #{datastore['name']} created"
|
482
|
+
#get([datastore['id']])
|
483
|
+
end
|
484
|
+
end
|
485
|
+
rescue RestClient::Exception => e
|
486
|
+
print_rest_exception(e, options)
|
487
|
+
exit 1
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
382
491
|
private
|
383
492
|
|
384
493
|
|
@@ -428,4 +537,7 @@ class Morpheus::Cli::CloudDatastoresCommand
|
|
428
537
|
end
|
429
538
|
end
|
430
539
|
|
540
|
+
def load_option_types_for_datastore_type(datastore_type)
|
541
|
+
return @storage_datastore_interface.load_type_options(datastore_type)
|
542
|
+
end
|
431
543
|
end
|
@@ -848,7 +848,6 @@ EOT
|
|
848
848
|
"Discovery" => lambda {|it| format_boolean it['hasDiscovery'] },
|
849
849
|
"Cloud Init" => lambda {|it| format_boolean it['hasCloudInit'] },
|
850
850
|
"Folders" => lambda {|it| format_boolean it['hasFolders'] },
|
851
|
-
"Floating Ips" => lambda {|it| format_boolean it['hasFloatingIps'] },
|
852
851
|
# "Marketplace" => lambda {|it| format_boolean it['hasMarketplace'] },
|
853
852
|
"Public Cloud" => lambda {|it| format_boolean(it['cloud'] == 'public') },
|
854
853
|
}
|
@@ -123,7 +123,6 @@ EOT
|
|
123
123
|
"Discovery" => lambda {|it| format_boolean it['hasDiscovery'] },
|
124
124
|
"Cloud Init" => lambda {|it| format_boolean it['hasCloudInit'] },
|
125
125
|
"Folders" => lambda {|it| format_boolean it['hasFolders'] },
|
126
|
-
"Floating Ips" => lambda {|it| format_boolean it['hasFloatingIps'] },
|
127
126
|
# "Marketplace" => lambda {|it| format_boolean it['hasMarketplace'] },
|
128
127
|
"Public Cloud" => lambda {|it| format_boolean(it['cloud'] == 'public') },
|
129
128
|
}
|
@@ -652,6 +652,10 @@ class Morpheus::Cli::Clusters
|
|
652
652
|
metadata_option_type = option_type_list.find {|type| type['fieldName'] == 'metadata' }
|
653
653
|
option_type_list = option_type_list.reject {|type| type['fieldName'] == 'metadata' }
|
654
654
|
|
655
|
+
# remove load balancer option_type, prompt manually
|
656
|
+
loadbalancer_option_type = option_type_list.find{|type| type['fieldName'] == 'loadBalancerId'}
|
657
|
+
option_type_list = option_type_list.reject { |type| type['fieldName'] == 'loadBalancerId' }
|
658
|
+
|
655
659
|
server_count = layout['serverCount']
|
656
660
|
|
657
661
|
# KLUDGE: google zone required for network selection
|
@@ -754,6 +758,37 @@ class Morpheus::Cli::Clusters
|
|
754
758
|
end
|
755
759
|
end
|
756
760
|
|
761
|
+
if loadbalancer_option_type
|
762
|
+
lb_payload = { computeTypeLayoutId: cluster_payload['layout']['id']}
|
763
|
+
load_balancer_id = prompt_cluster_load_balancer(cluster_payload, options)
|
764
|
+
if load_balancer_id != false
|
765
|
+
lb_payload['loadBalancerId'] = load_balancer_id
|
766
|
+
lb_payload['loadBalancerInstanceId'] = -1
|
767
|
+
lb_port_result = @clusters_interface.load_balancer_port(lb_payload)
|
768
|
+
lb_options = lb_port_result['optionTypes']
|
769
|
+
lb_option_results = Morpheus::Cli::OptionTypes.prompt(lb_options, options[:options], @api_client, api_params)
|
770
|
+
|
771
|
+
load_balancer_payload = {
|
772
|
+
port: lb_port_result.dig('loadBalancerPort', 'externalPort'),
|
773
|
+
enabled: lb_port_result.dig('loadBalancer', 'enabled'),
|
774
|
+
loadBalancerId: load_balancer_id,
|
775
|
+
backendPort: lb_port_result.dig('loadBalancerPort', 'internalPort'),
|
776
|
+
loadBalancer: { id: load_balancer_id },
|
777
|
+
vipPool: (lb_option_results.dig('domain', 'vipPool') == 'none') ? nil : lb_option_results.dig('domain', 'vipPool'),
|
778
|
+
vipAddress: lb_option_results.dig('domain', 'vipAddress'),
|
779
|
+
externalAddress: (lb_port_result.dig('loadBalancer', 'externalAddress') == true ) ? 'on': 'off',
|
780
|
+
vipShared: (lb_port_result.dig('loadBalancer', 'vipShared') == true ) ? 'on' : 'off',
|
781
|
+
vipProtocol: lb_port_result.dig('loadBalancerPort', 'loadBalanceProtocol'),
|
782
|
+
instanceId: lb_payload['loadBalancerInstanceId'],
|
783
|
+
name: lb_option_results['vipName']
|
784
|
+
}
|
785
|
+
if lb_option_results['domain']
|
786
|
+
load_balancer_payload.merge!(lb_option_results['domain']) { |key, old_val, new_val| old_val }
|
787
|
+
end
|
788
|
+
cluster_payload['lbInstances'] = [load_balancer_payload.compact!]
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
757
792
|
cluster_payload['server'] = server_payload
|
758
793
|
payload = {'cluster' => cluster_payload}
|
759
794
|
end
|
@@ -4248,7 +4283,7 @@ class Morpheus::Cli::Clusters
|
|
4248
4283
|
end
|
4249
4284
|
|
4250
4285
|
def find_layout_by_name(name)
|
4251
|
-
@cluster_layouts_interface.list({phrase:name}).find { it['name'].downcase == name.downcase || it['code'].downcase == name.downcase }
|
4286
|
+
@cluster_layouts_interface.list({phrase:name}).find { |it| it['name'].downcase == name.downcase || it['code'].downcase == name.downcase }
|
4252
4287
|
end
|
4253
4288
|
|
4254
4289
|
def layouts_for_dropdown(zone_id, group_type_id)
|
@@ -4,6 +4,7 @@ class Morpheus::Cli::ContainersCommand
|
|
4
4
|
include Morpheus::Cli::CliCommand
|
5
5
|
include Morpheus::Cli::ProvisioningHelper
|
6
6
|
include Morpheus::Cli::LogsHelper
|
7
|
+
include Morpheus::Cli::InfrastructureHelper
|
7
8
|
|
8
9
|
set_command_name :containers
|
9
10
|
set_command_description "View and manage containers (nodes)."
|
@@ -18,6 +19,7 @@ class Morpheus::Cli::ContainersCommand
|
|
18
19
|
@logs_interface = @api_client.logs
|
19
20
|
@execution_request_interface = @api_client.execution_request
|
20
21
|
@clouds_interface = @api_client.clouds
|
22
|
+
@network_server_types_interface = @api_client.network_server_types
|
21
23
|
end
|
22
24
|
|
23
25
|
def handle(args)
|
@@ -799,14 +801,14 @@ EOT
|
|
799
801
|
connect(options)
|
800
802
|
container = find_container_by_id(args[0])
|
801
803
|
return 1 if container.nil?
|
802
|
-
|
803
|
-
if !
|
804
|
-
raise_command_error "
|
804
|
+
network_server_type = load_container_network_server_type(container)
|
805
|
+
if !network_server_type['hasFloatingIps']
|
806
|
+
raise_command_error "Network Server Type #{network_server_type['name']} does support floating IPs."
|
805
807
|
end
|
806
808
|
payload = parse_payload(options)
|
807
809
|
if payload.nil?
|
808
810
|
payload = parse_passed_options(options)
|
809
|
-
attach_floating_ip_option_types =
|
811
|
+
attach_floating_ip_option_types = network_server_type['floatingIpTypes']
|
810
812
|
if attach_floating_ip_option_types && !attach_floating_ip_option_types.empty?
|
811
813
|
if options[:ip]
|
812
814
|
floating_ip = options[:ip].to_s.sub(/\Aip\-/i, '')
|
@@ -829,7 +831,7 @@ EOT
|
|
829
831
|
# payload.deep_merge!({'container' => v_prompt})
|
830
832
|
payload.deep_merge!(v_prompt)
|
831
833
|
else
|
832
|
-
# raise_command_error "
|
834
|
+
# raise_command_error "Network Server Type #{network_server_type['name']} does not defined any floating IP inputs."
|
833
835
|
end
|
834
836
|
end
|
835
837
|
confirm!("Are you sure you would like to attach this floating IP to container #{container['id']}?", options)
|
@@ -861,9 +863,9 @@ EOT
|
|
861
863
|
connect(options)
|
862
864
|
container = find_container_by_id(args[0])
|
863
865
|
return 1 if container.nil?
|
864
|
-
|
865
|
-
if !
|
866
|
-
raise_command_error "
|
866
|
+
network_server_type = load_container_network_server_type(container)
|
867
|
+
if !network_server_type['hasFloatingIps']
|
868
|
+
raise_command_error "Network Type #{network_server_type['name']} does support floating IPs."
|
867
869
|
end
|
868
870
|
payload = parse_payload(options)
|
869
871
|
if payload.nil?
|
@@ -948,18 +950,18 @@ EOT
|
|
948
950
|
return provision_type
|
949
951
|
end
|
950
952
|
|
951
|
-
def
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
953
|
+
def load_container_network_server_type(container)
|
954
|
+
cloud = find_cloud_by_id(container['cloud']['id'])
|
955
|
+
network_server_type_code = cloud['networkServer']['type'] rescue nil
|
956
|
+
network_server_type = nil
|
957
|
+
if network_server_type_code
|
958
|
+
network_server_type = @network_server_types_interface.list({code:network_server_type_code})['networkServerTypes'][0]
|
959
|
+
if network_server_type.nil?
|
960
|
+
raise_command_error "Network Server Type not found by code #{network_server_type_code}"
|
961
|
+
end
|
959
962
|
else
|
960
|
-
|
963
|
+
raise_command_error "Unable to determine network server type for container #{container['id']}"
|
961
964
|
end
|
962
|
-
return
|
965
|
+
return network_server_type
|
963
966
|
end
|
964
|
-
|
965
967
|
end
|
@@ -12,7 +12,8 @@ class Morpheus::Cli::Hosts
|
|
12
12
|
{:'types' => :list_types},
|
13
13
|
{:exec => :execution_request},
|
14
14
|
:wiki, :update_wiki,
|
15
|
-
:maintenance, :leave_maintenance, :placement
|
15
|
+
:maintenance, :leave_maintenance, :placement,
|
16
|
+
:list_devices, :assign_device, :detach_device, :attach_device
|
16
17
|
alias_subcommand :details, :get
|
17
18
|
set_default_subcommand :list
|
18
19
|
|
@@ -2281,7 +2282,7 @@ EOT
|
|
2281
2282
|
end
|
2282
2283
|
payload[:server][:deleteLocalData] = options.fetch(:deleteLocalData) do
|
2283
2284
|
prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'deleteLocalData', 'fieldLabel' => 'Delete Local Data', 'type' => 'checkbox', 'defaultValue' => false, 'required' => false}], options, @api_client, {})
|
2284
|
-
prompt['
|
2285
|
+
prompt['deleteLocalData'] == 'on'
|
2285
2286
|
end
|
2286
2287
|
end
|
2287
2288
|
|
@@ -2354,7 +2355,7 @@ EOT
|
|
2354
2355
|
end
|
2355
2356
|
json_response = @servers_interface.leave_maintenance(server['id'], payload, params)
|
2356
2357
|
render_response(json_response, options) do
|
2357
|
-
print_green_success "Maintenance
|
2358
|
+
print_green_success "Left Maintenance Mode for host #{server['name']}"
|
2358
2359
|
#get([server['id']])
|
2359
2360
|
end
|
2360
2361
|
return 0, nil
|
@@ -2418,6 +2419,24 @@ EOT
|
|
2418
2419
|
return 0, nil
|
2419
2420
|
end
|
2420
2421
|
|
2422
|
+
## Server Devices
|
2423
|
+
|
2424
|
+
def list_devices(args)
|
2425
|
+
Morpheus::Cli::ServerDevicesCommand.new.list(args)
|
2426
|
+
end
|
2427
|
+
|
2428
|
+
def assign_device(args)
|
2429
|
+
Morpheus::Cli::ServerDevicesCommand.new.assign(args)
|
2430
|
+
end
|
2431
|
+
|
2432
|
+
def attach_device(args)
|
2433
|
+
Morpheus::Cli::ServerDevicesCommand.new.attach(args)
|
2434
|
+
end
|
2435
|
+
|
2436
|
+
def detach_device(args)
|
2437
|
+
Morpheus::Cli::ServerDevicesCommand.new.detach(args)
|
2438
|
+
end
|
2439
|
+
|
2421
2440
|
private
|
2422
2441
|
|
2423
2442
|
def find_host_by_id(id)
|
@@ -2440,7 +2459,9 @@ EOT
|
|
2440
2459
|
print_red_alert "Server not found by name #{name}"
|
2441
2460
|
exit 1
|
2442
2461
|
elsif results['servers'].size > 1
|
2443
|
-
print_red_alert "Multiple Servers exist with the name #{name}
|
2462
|
+
print_red_alert "Multiple Servers exist with the name '#{name}'"
|
2463
|
+
puts_error as_pretty_table(results['servers'], [:id, :name], {color:red})
|
2464
|
+
print_red_alert "Try using ID instead"
|
2444
2465
|
exit 1
|
2445
2466
|
end
|
2446
2467
|
return results['servers'][0]
|
@@ -316,7 +316,12 @@ class Morpheus::Cli::License
|
|
316
316
|
"Start Date" => lambda {|it| format_local_dt(it['startDate']) },
|
317
317
|
"End Date" => lambda {|it|
|
318
318
|
if it['endDate']
|
319
|
-
|
319
|
+
end_date = parse_time(it['endDate']) rescue nil
|
320
|
+
if end_date && Time.now > end_date
|
321
|
+
format_local_dt(it['endDate']).to_s + red + ' (expired)' + cyan
|
322
|
+
else
|
323
|
+
format_local_dt(it['endDate']).to_s + ' (' + format_duration(Time.now, it['endDate']).to_s + ')'
|
324
|
+
end
|
320
325
|
else
|
321
326
|
'Never'
|
322
327
|
end
|
@@ -424,7 +429,12 @@ class Morpheus::Cli::License
|
|
424
429
|
"Start Date" => lambda {|it| format_local_dt(it['startDate']) },
|
425
430
|
"End Date" => lambda {|it|
|
426
431
|
if it['endDate']
|
427
|
-
|
432
|
+
end_date = parse_time(it['endDate']) rescue nil
|
433
|
+
if end_date && Time.now > end_date
|
434
|
+
format_local_dt(it['endDate']).to_s + red + ' (expired)' + cyan
|
435
|
+
else
|
436
|
+
format_local_dt(it['endDate']).to_s + ' (' + format_duration(Time.now, it['endDate']).to_s + ')'
|
437
|
+
end
|
428
438
|
else
|
429
439
|
'Never'
|
430
440
|
end
|