morpheus-cli 6.0.1 → 6.1.0
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/backups_interface.rb +4 -0
- data/lib/morpheus/api/network_services_interface.rb +16 -0
- data/lib/morpheus/cli/cli_command.rb +115 -2
- data/lib/morpheus/cli/commands/backup_jobs_command.rb +3 -7
- data/lib/morpheus/cli/commands/backups_command.rb +133 -19
- data/lib/morpheus/cli/commands/curl_command.rb +5 -1
- data/lib/morpheus/cli/commands/invoices_command.rb +2 -2
- data/lib/morpheus/cli/commands/load_balancers.rb +5 -0
- data/lib/morpheus/cli/commands/network_services_command.rb +60 -1
- data/lib/morpheus/cli/commands/networks_command.rb +31 -0
- data/lib/morpheus/cli/commands/service_catalog_command.rb +77 -103
- data/lib/morpheus/cli/commands/service_plans_command.rb +34 -0
- data/lib/morpheus/cli/mixins/backups_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +10 -4
- data/lib/morpheus/cli/option_types.rb +3 -0
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57fffe54fd7ee43a1bd35bb15703bb85edca71ec59758374d9556d1743338f90
|
4
|
+
data.tar.gz: 3472c73b3bc3bf91b84e437eb593b367d86cf8aa32439d370eb6c15de40674fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e87cbd833e3633ddf03129d7eabfdb11b12a2956f6c6f66b43a5d42fd41cbb7a209085b6f1008e89ffbcf98251b8d6fc8e0e0d1b845a952b863c0f6add10386b
|
7
|
+
data.tar.gz: f51460150a7ef907e7c679d75c54830405ef18323c26aa0bcf1bb158c03ff7e05c53114b0e9df2edf4bba337ec6c843871f0fd3bd0ab9ad15ce9ceed836f2bfe
|
data/Dockerfile
CHANGED
@@ -6,6 +6,10 @@ class Morpheus::BackupsInterface < Morpheus::RestInterface
|
|
6
6
|
"/api/backups"
|
7
7
|
end
|
8
8
|
|
9
|
+
def create_options(payload, params={}, headers={})
|
10
|
+
execute(method: :post, url: "#{base_path}/create", params: params, payload: payload, headers: headers)
|
11
|
+
end
|
12
|
+
|
9
13
|
def summary(params={})
|
10
14
|
execute(method: :get, url: "#{base_path}/summary", params: params)
|
11
15
|
end
|
@@ -23,6 +23,22 @@ class Morpheus::NetworkServicesInterface < Morpheus::APIClient
|
|
23
23
|
execute(method: :get, url: url, headers: headers)
|
24
24
|
end
|
25
25
|
|
26
|
+
def get_server(server_id)
|
27
|
+
execute(method: :get, url: "#{@base_url}/api/networks/servers/#{server_id}", params: {}, headers: {})
|
28
|
+
end
|
29
|
+
|
30
|
+
def list_servers()
|
31
|
+
execute(method: :get, url: "#{@base_url}/api/networks/servers", params: {}, headers: {})
|
32
|
+
end
|
33
|
+
|
34
|
+
def refresh(server_id)
|
35
|
+
url = "#{@base_url}/api/networks/servers/#{server_id}/refresh"
|
36
|
+
|
37
|
+
headers = { :params => {}, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
38
|
+
opts = {method: :post, url: url, headers: headers}
|
39
|
+
execute(opts)
|
40
|
+
end
|
41
|
+
|
26
42
|
# def create(payload)
|
27
43
|
# url = "#{@base_url}/api/networks/services"
|
28
44
|
# headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
@@ -293,6 +293,7 @@ module Morpheus
|
|
293
293
|
#opts.separator ""
|
294
294
|
# opts.separator "Common options:"
|
295
295
|
option_keys = includes.clone
|
296
|
+
all_option_keys = option_keys.dup
|
296
297
|
# todo: support --quiet everywhere
|
297
298
|
# turn on some options all the time..
|
298
299
|
# unless command_name == "shell"
|
@@ -445,7 +446,8 @@ module Morpheus
|
|
445
446
|
raise ::OptionParser::InvalidOption.new("Failed to parse payload file: #{payload_file} Error: #{ex.message}")
|
446
447
|
end
|
447
448
|
end
|
448
|
-
opts.on('--payload-dir DIRECTORY', String, "Payload from a local directory containing 1-N JSON or YAML files, skip all prompting.") do |val|
|
449
|
+
opts.on('--payload-dir DIRECTORY', String, "Payload from a local directory containing 1-N JSON or YAML files, skip all prompting. This makes one request, merging all the files into a single payload.") do |val|
|
450
|
+
print_error yellow,"[DEPRECATED] The option `--payload-dir` is deprecated and will be removed. Use `--payloads` to make requests for each file in a directory.",reset,"\n"
|
449
451
|
options[:payload_dir] = val.to_s
|
450
452
|
payload_dir = File.expand_path(options[:payload_dir])
|
451
453
|
if !Dir.exist?(payload_dir) || !File.directory?(payload_dir)
|
@@ -476,6 +478,7 @@ module Morpheus
|
|
476
478
|
raise ::OptionParser::InvalidOption.new("Failed to parse payload file: #{payload_file} Error: #{ex.message}")
|
477
479
|
end
|
478
480
|
end
|
481
|
+
opts.add_hidden_option('--payload-dir')
|
479
482
|
opts.on('--payload-json JSON', String, "Payload JSON, skip all prompting") do |val|
|
480
483
|
begin
|
481
484
|
options[:payload] = JSON.parse(val.to_s)
|
@@ -490,7 +493,69 @@ module Morpheus
|
|
490
493
|
raise ::OptionParser::InvalidOption.new("Failed to parse payload as YAML. Error: #{ex.message}")
|
491
494
|
end
|
492
495
|
end
|
493
|
-
|
496
|
+
# --payloads test-data/item*.json
|
497
|
+
opts.on('--payloads PATH', String, "Payload(s) from one or more local JSON or YAML files, skip all prompting and execute the request 1-N times, once for each file. PATH can be a directory or a file pattern.") do |val|
|
498
|
+
# maybe use parse_array(val) to support csv..
|
499
|
+
# find files matching PATH
|
500
|
+
# todo: probably support recursive... can be done with '**/*.json' now though.
|
501
|
+
if val.to_s.strip.empty?
|
502
|
+
raise ::OptionParser::InvalidOption.new("PATH must be provided as directory, file or pattern to find JSON or YAML files.")
|
503
|
+
end
|
504
|
+
filepath = File.expand_path(val.to_s.strip)
|
505
|
+
files = []
|
506
|
+
if File.directory?(filepath)
|
507
|
+
# passed the name of a directory, include all the JSON and YAML files directly under it
|
508
|
+
Dir.glob(File.join(filepath, "*")).each do |file|
|
509
|
+
if File.file?(file) && ['.json','.yaml','.yml'].include?(File.extname(file))
|
510
|
+
files << file
|
511
|
+
end
|
512
|
+
end
|
513
|
+
if files.empty?
|
514
|
+
raise ::OptionParser::InvalidOption.new("Failed to find any .json or .yaml files under the directory: #{filepath}")
|
515
|
+
end
|
516
|
+
elsif File.file?(filepath)
|
517
|
+
# passed the name of a file
|
518
|
+
files << filepath
|
519
|
+
else
|
520
|
+
# assume it is a pattern to find files with
|
521
|
+
files = Dir.glob(filepath)
|
522
|
+
if files.empty?
|
523
|
+
raise ::OptionParser::InvalidOption.new("Failed to find any files matching path: #{filepath}")
|
524
|
+
end
|
525
|
+
end
|
526
|
+
# parse files as JSON or YAML
|
527
|
+
options[:payload_files] ||= []
|
528
|
+
options[:payloads] ||= []
|
529
|
+
files.each do |file|
|
530
|
+
if options[:payload_files].include?(file)
|
531
|
+
next
|
532
|
+
else
|
533
|
+
options[:payload_files] << file
|
534
|
+
end
|
535
|
+
payload = nil
|
536
|
+
begin
|
537
|
+
payload_file = File.expand_path(file)
|
538
|
+
if !File.exist?(payload_file) || !File.file?(payload_file)
|
539
|
+
raise ::OptionParser::InvalidOption.new("File not found: #{payload_file}")
|
540
|
+
end
|
541
|
+
# todo: could use parse_json_or_yaml()
|
542
|
+
payload = nil
|
543
|
+
if payload_file =~ /\.ya?ml\Z/
|
544
|
+
payload = YAML.load_file(payload_file)
|
545
|
+
else
|
546
|
+
payload = JSON.parse(File.read(payload_file))
|
547
|
+
end
|
548
|
+
options[:payloads] << payload
|
549
|
+
rescue => ex
|
550
|
+
raise ::OptionParser::InvalidOption.new("Failed to parse payload file: #{payload_file} Error: #{ex.message}")
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end if all_option_keys.include?(:payloads)
|
554
|
+
opts.on('--payloads-ignore-error', "Continue processing payloads if an error occurs. The default behavior is to stop processing when an error occurs.") do
|
555
|
+
options[:payloads_ignore_error] = true
|
556
|
+
end if all_option_keys.include?(:payloads)
|
557
|
+
when :payloads
|
558
|
+
# added under when :payloads... just need it here to avoid unknown key error
|
494
559
|
when :list
|
495
560
|
opts.on( '-m', '--max MAX', "Max Results" ) do |val|
|
496
561
|
# api supports max=-1 for all at the moment..
|
@@ -1435,6 +1500,54 @@ module Morpheus
|
|
1435
1500
|
payload
|
1436
1501
|
end
|
1437
1502
|
|
1503
|
+
def parse_payloads(options={}, object_key=nil, &block)
|
1504
|
+
payloads = []
|
1505
|
+
if options[:payload]
|
1506
|
+
# --payload option was used
|
1507
|
+
payload = options[:payload]
|
1508
|
+
# support -O OPTION switch on top of --payload
|
1509
|
+
apply_options(payload, options, object_key)
|
1510
|
+
payloads << payload
|
1511
|
+
elsif options[:payloads]
|
1512
|
+
# --payloads option was used
|
1513
|
+
payloads = options[:payloads]
|
1514
|
+
# payloads.each { |it| apply_options(it, options, object_key) }
|
1515
|
+
else
|
1516
|
+
# default is to construct one using the block
|
1517
|
+
payload = {}
|
1518
|
+
apply_options(payload, options, object_key)
|
1519
|
+
if block_given?
|
1520
|
+
result = yield payload
|
1521
|
+
#payload = result if result
|
1522
|
+
end
|
1523
|
+
payloads << payload
|
1524
|
+
end
|
1525
|
+
return payloads
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
def process_payloads(payloads, options, &block)
|
1529
|
+
if !payloads.is_a?(Array) || payloads.compact.empty?
|
1530
|
+
raise "process_payloads() requires an Array of at least one payload and instead got: (#{payloads.class}) #{payloads.inspect}"
|
1531
|
+
end
|
1532
|
+
results = []
|
1533
|
+
payloads.each do |payload|
|
1534
|
+
begin
|
1535
|
+
result = yield payload
|
1536
|
+
results << [0, nil]
|
1537
|
+
rescue => e
|
1538
|
+
if options[:payloads_ignore_error]
|
1539
|
+
# results << [1, e.message]
|
1540
|
+
result = Morpheus::Cli::ErrorHandler.new(my_terminal.stderr).handle_error(e) # lol
|
1541
|
+
results << result
|
1542
|
+
# continue
|
1543
|
+
else
|
1544
|
+
raise e
|
1545
|
+
end
|
1546
|
+
end
|
1547
|
+
end
|
1548
|
+
return results.last
|
1549
|
+
end
|
1550
|
+
|
1438
1551
|
def build_payload(options, object_key=nil)
|
1439
1552
|
payload = {}
|
1440
1553
|
if options[:payload]
|
@@ -10,7 +10,7 @@ class Morpheus::Cli::BackupJobsCommand
|
|
10
10
|
|
11
11
|
set_command_name :'backup-jobs'
|
12
12
|
|
13
|
-
register_subcommands :list, :get
|
13
|
+
register_subcommands :list, :get, :add, :update, :remove, :run
|
14
14
|
|
15
15
|
def connect(opts)
|
16
16
|
@api_client = establish_remote_appliance_connection(opts)
|
@@ -55,11 +55,7 @@ class Morpheus::Cli::BackupJobsCommand
|
|
55
55
|
end
|
56
56
|
print reset,"\n"
|
57
57
|
end
|
58
|
-
|
59
|
-
return 1, "no backup jobs found"
|
60
|
-
else
|
61
|
-
return 0, nil
|
62
|
-
end
|
58
|
+
return 0, nil
|
63
59
|
end
|
64
60
|
|
65
61
|
def get(args)
|
@@ -250,7 +246,7 @@ EOT
|
|
250
246
|
def add_backup_job_option_types
|
251
247
|
[
|
252
248
|
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
253
|
-
{'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'required' =>
|
249
|
+
#{'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
254
250
|
{'fieldName' => 'retentionCount', 'fieldLabel' => 'Retention Count', 'type' => 'number', 'displayOrder' => 3},
|
255
251
|
{'fieldName' => 'scheduleId', 'fieldLabel' => 'Schedule', 'type' => 'select', 'optionSource' => 'executeSchedules', 'displayOrder' => 4}, # should use jobSchedules instead maybe? do we support manual schedules for backups?
|
256
252
|
]
|
@@ -10,12 +10,14 @@ class Morpheus::Cli::BackupsCommand
|
|
10
10
|
|
11
11
|
set_command_name :'backups'
|
12
12
|
|
13
|
-
register_subcommands :list, :get
|
13
|
+
register_subcommands :list, :get, :add, :update, :remove, :run, :restore
|
14
14
|
|
15
15
|
def connect(opts)
|
16
16
|
@api_client = establish_remote_appliance_connection(opts)
|
17
17
|
@backups_interface = @api_client.backups
|
18
18
|
@backup_jobs_interface = @api_client.backup_jobs
|
19
|
+
@instances_interface = @api_client.instances
|
20
|
+
@servers_interface = @api_client.servers
|
19
21
|
end
|
20
22
|
|
21
23
|
def handle(args)
|
@@ -50,16 +52,12 @@ class Morpheus::Cli::BackupsCommand
|
|
50
52
|
if backups.empty?
|
51
53
|
print cyan,"No backups found.",reset,"\n"
|
52
54
|
else
|
53
|
-
print as_pretty_table(backups,
|
55
|
+
print as_pretty_table(backups, backup_list_column_definitions.upcase_keys!, options)
|
54
56
|
print_results_pagination(json_response)
|
55
57
|
end
|
56
58
|
print reset,"\n"
|
57
59
|
end
|
58
|
-
|
59
|
-
return 1, "no backups found"
|
60
|
-
else
|
61
|
-
return 0, nil
|
62
|
-
end
|
60
|
+
return 0, nil
|
63
61
|
end
|
64
62
|
|
65
63
|
def get(args)
|
@@ -83,6 +81,13 @@ EOT
|
|
83
81
|
end
|
84
82
|
|
85
83
|
def _get(id, params, options)
|
84
|
+
if id.to_s !~ /\A\d{1,}\Z/
|
85
|
+
record = find_by_name(:backup, id)
|
86
|
+
if record.nil?
|
87
|
+
return 1, "Backup not found for '#{id}'"
|
88
|
+
end
|
89
|
+
id = record['id']
|
90
|
+
end
|
86
91
|
@backups_interface.setopts(options)
|
87
92
|
if options[:dry_run]
|
88
93
|
print_dry_run @backups_interface.dry.get(id, params)
|
@@ -93,7 +98,11 @@ EOT
|
|
93
98
|
render_response(json_response, options, 'backup') do
|
94
99
|
print_h1 "Backup Details", [], options
|
95
100
|
print cyan
|
96
|
-
|
101
|
+
columns = backup_column_definitions
|
102
|
+
columns.delete("Instance") if backup['instance'].nil?
|
103
|
+
columns.delete("Container ID") if backup['containerId'].nil?
|
104
|
+
columns.delete("Host") if backup['server'].nil?
|
105
|
+
print_description_list(columns, backup, options)
|
97
106
|
print reset,"\n"
|
98
107
|
end
|
99
108
|
return 0, nil
|
@@ -104,8 +113,26 @@ EOT
|
|
104
113
|
params = {}
|
105
114
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
106
115
|
opts.banner = subcommand_usage("[name] [options]")
|
107
|
-
build_option_type_options(opts, options, add_backup_option_types)
|
108
|
-
|
116
|
+
# build_option_type_options(opts, options, add_backup_option_types)
|
117
|
+
opts.on('--source VALUE', String, "Backup Source: instance, host or provider") do |val|
|
118
|
+
options[:options]['source'] = val
|
119
|
+
end
|
120
|
+
opts.on('--instance VALUE', String, "Instance Name or ID") do |val|
|
121
|
+
options[:options]['source'] = 'instance'
|
122
|
+
options[:options]['instanceId'] = val
|
123
|
+
end
|
124
|
+
opts.on('--host VALUE', String, "Host Name or ID") do |val|
|
125
|
+
options[:options]['source'] = 'server'
|
126
|
+
options[:options]['serverId'] = val
|
127
|
+
end
|
128
|
+
opts.on('--server VALUE', String, "alias for --host") do |val|
|
129
|
+
options[:options]['source'] = 'server'
|
130
|
+
options[:options]['serverId'] = val
|
131
|
+
end
|
132
|
+
opts.add_hidden_option('--server')
|
133
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
134
|
+
options[:options]['name'] = val
|
135
|
+
end
|
109
136
|
build_standard_add_options(opts, options)
|
110
137
|
opts.footer = <<-EOT
|
111
138
|
Create a new backup.
|
@@ -121,11 +148,74 @@ EOT
|
|
121
148
|
payload.deep_merge!({'backup' => parse_passed_options(options)})
|
122
149
|
else
|
123
150
|
payload.deep_merge!({'backup' => parse_passed_options(options)})
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
151
|
+
|
152
|
+
location_type = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'source', 'fieldLabel' => 'Source', 'type' => 'select', 'selectOptions' => [{'name' => 'Instance', 'value' => 'instance'}, {'name' => 'Host', 'value' => 'server'}, {'name' => 'Provider', 'value' => 'provider'}], 'defaultValue' => 'instance', 'required' => true, 'description' => 'Where is the backup located?'}], options[:options], @api_client)['source']
|
153
|
+
params['locationType'] = location_type
|
154
|
+
if location_type == 'instance'
|
155
|
+
# Instance
|
156
|
+
avail_instances = @instances_interface.list({max:10000})['instances'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
157
|
+
params['instanceId'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instanceId', 'fieldLabel' => 'Instance', 'type' => 'select', 'selectOptions' => avail_instances, 'required' => true}], options[:options], @api_client)['instanceId']
|
158
|
+
# Name
|
159
|
+
params['name'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Backup Name'}], options[:options], @api_client)['name']
|
160
|
+
elsif location_type == 'server'
|
161
|
+
# Server
|
162
|
+
avail_servers = @servers_interface.list({max:10000, 'vmHypervisor' => nil, 'containerHypervisor' => nil})['servers'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
163
|
+
params['serverId'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'serverId', 'fieldLabel' => 'Host', 'type' => 'select', 'selectOptions' => avail_servers, 'required' => true}], options[:options], @api_client)['serverId']
|
164
|
+
# Name
|
165
|
+
params['name'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Backup Name'}], options[:options], @api_client)['name']
|
166
|
+
elsif location_type == 'provider'
|
167
|
+
# todo: prompt for provider inputs
|
168
|
+
# sourceProviderId
|
169
|
+
# storageProvider
|
170
|
+
end
|
171
|
+
# POST to /create to get available option info for containers, backupTypes, backupProviderTypes, etc.
|
172
|
+
payload['backup'].deep_merge!(params)
|
173
|
+
create_results = @backups_interface.create_options(payload)
|
174
|
+
|
175
|
+
if location_type == 'instance' || location_type == 'server'
|
176
|
+
if location_type == 'instance'
|
177
|
+
# Container
|
178
|
+
avail_containers = (create_results['containers'] || []).collect {|it| {'name' => it['name'], 'value' => it['id']} }
|
179
|
+
if avail_containers.empty?
|
180
|
+
raise_command_error "No available containers found for selected instance"
|
181
|
+
else
|
182
|
+
params['containerId'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'containerId', 'fieldLabel' => 'Container', 'type' => 'select', 'selectOptions' => avail_containers, 'defaultValue' => avail_containers[0] ? avail_containers[0]['name'] : nil, 'required' => true}], options[:options], @api_client)['containerId']
|
183
|
+
end
|
184
|
+
elsif location_type == 'server'
|
185
|
+
|
186
|
+
|
187
|
+
end
|
188
|
+
# Backup Type
|
189
|
+
avail_backup_types = (create_results['backupTypes'] || []).collect {|it| {'name' => it['name'], 'value' => it['code']} }
|
190
|
+
if avail_backup_types.empty?
|
191
|
+
raise_command_error "No available backup types found"
|
192
|
+
else
|
193
|
+
params['backupType'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'backupType', 'fieldLabel' => 'Backup Type', 'type' => 'select', 'selectOptions' => avail_backup_types, 'defaultValue' => avail_backup_types[0] ? avail_backup_types[0]['name'] : nil, 'required' => true}], options[:options], @api_client)['backupType']
|
194
|
+
end
|
195
|
+
|
196
|
+
# Job / Schedule
|
197
|
+
params['jobAction'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobAction', 'fieldLabel' => 'Backup Job Type', 'type' => 'select', 'optionSource' => 'backupJobActions', 'required' => true, 'defaultValue' => 'new'}], options[:options], @api_client)['jobAction']
|
198
|
+
if params['jobAction'] == 'new'
|
199
|
+
params['jobName'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobName', 'fieldLabel' => 'Job Name', 'type' => 'text', 'required' => false, 'defaultValue' => nil}], options[:options], @api_client)['jobName']
|
200
|
+
default_retention_count = create_results['backup'] ? create_results['backup']['retentionCount'] : nil
|
201
|
+
params['retentionCount'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'retentionCount', 'fieldLabel' => 'Retention Count', 'type' => 'number', 'required' => false, 'defaultValue' => default_retention_count}], options[:options], @api_client)['retentionCount']
|
202
|
+
params['jobSchedule'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobSchedule', 'fieldLabel' => 'Backup Schedule', 'type' => 'select', 'optionSource' => 'executeSchedules', 'required' => true}], options[:options], @api_client)['jobSchedule']
|
203
|
+
elsif params['jobAction'] == 'clone'
|
204
|
+
params['jobId'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobId', 'fieldLabel' => 'Backup Job', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
|
205
|
+
@backup_jobs_interface.list({max:10000})['jobs'].collect {|backup_job|
|
206
|
+
{'name' => backup_job['name'], 'value' => backup_job['id'], 'id' => backup_job['id']}
|
207
|
+
}
|
208
|
+
}, 'required' => true}], options[:options], @api_client)['jobId']
|
209
|
+
params['jobName'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobName', 'fieldLabel' => 'Job Name', 'type' => 'text', 'required' => false, 'defaultValue' => nil}], options[:options], @api_client)['jobName']
|
210
|
+
elsif params['jobAction'] == 'addTo'
|
211
|
+
params['jobId'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobId', 'fieldLabel' => 'Backup Job', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
|
212
|
+
@backup_jobs_interface.list({max:10000})['jobs'].collect {|backup_job|
|
213
|
+
{'name' => backup_job['name'], 'value' => backup_job['id'], 'id' => backup_job['id']}
|
214
|
+
}
|
215
|
+
}, 'required' => true}], options[:options], @api_client)['jobId']
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
129
219
|
payload['backup'].deep_merge!(params)
|
130
220
|
end
|
131
221
|
@backups_interface.setopts(options)
|
@@ -226,10 +316,33 @@ EOT
|
|
226
316
|
|
227
317
|
private
|
228
318
|
|
319
|
+
def backup_list_column_definitions()
|
320
|
+
{
|
321
|
+
"ID" => 'id',
|
322
|
+
"Name" => 'name',
|
323
|
+
"Schedule" => lambda {|it| it['schedule']['name'] rescue '' },
|
324
|
+
"Backup Job" => lambda {|it| it['job']['name'] rescue '' },
|
325
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
326
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
327
|
+
}
|
328
|
+
end
|
329
|
+
|
229
330
|
def backup_column_definitions()
|
230
331
|
{
|
231
332
|
"ID" => 'id',
|
232
333
|
"Name" => 'name',
|
334
|
+
"Location Type" => lambda {|it|
|
335
|
+
if it['locationType'] == "instance"
|
336
|
+
"Instance"
|
337
|
+
elsif it['locationType'] == "server"
|
338
|
+
"Host"
|
339
|
+
elsif it['locationType'] == "storage"
|
340
|
+
"Provider"
|
341
|
+
end
|
342
|
+
},
|
343
|
+
"Instance" => lambda {|it| it['instance']['name'] rescue '' },
|
344
|
+
"Container ID" => lambda {|it| it['containerId'] rescue '' },
|
345
|
+
"Host" => lambda {|it| it['server']['name'] rescue '' },
|
233
346
|
"Schedule" => lambda {|it| it['schedule']['name'] rescue '' },
|
234
347
|
"Backup Job" => lambda {|it| it['job']['name'] rescue '' },
|
235
348
|
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
@@ -240,13 +353,14 @@ EOT
|
|
240
353
|
# this is not so simple, need to first choose select instance, host or provider
|
241
354
|
def add_backup_option_types
|
242
355
|
[
|
243
|
-
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true
|
356
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
|
357
|
+
{'fieldName' => 'backupType', 'fieldLabel' => 'Backup Type', 'type' => 'select', 'optionSource' => 'backupTypes', 'required' => true},
|
358
|
+
{'fieldName' => 'jobAction', 'fieldLabel' => 'Backup Job Type', 'type' => 'select', 'optionSource' => 'backupJobActions', 'required' => true},
|
244
359
|
{'fieldName' => 'jobId', 'fieldLabel' => 'Backup Job', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
|
245
|
-
|
246
|
-
@backup_jobs_interface.list({max:10000})['backupJobs'].collect {|backup_job|
|
360
|
+
@backup_jobs_interface.list({max:10000})['jobs'].collect {|backup_job|
|
247
361
|
{'name' => backup_job['name'], 'value' => backup_job['id'], 'id' => backup_job['id']}
|
248
362
|
}
|
249
|
-
}, 'required' => true
|
363
|
+
}, 'required' => true},
|
250
364
|
]
|
251
365
|
end
|
252
366
|
|
@@ -27,7 +27,11 @@ class Morpheus::Cli::CurlCommand
|
|
27
27
|
curl_method = "DELETE"
|
28
28
|
end
|
29
29
|
opts.on( '--data DATA', String, "HTTP request body for use with POST and PUT, typically JSON." ) do |val|
|
30
|
-
|
30
|
+
begin
|
31
|
+
options[:payload] = JSON.parse(val.to_s)
|
32
|
+
rescue => ex
|
33
|
+
raise ::OptionParser::InvalidOption.new("Failed to parse payload as JSON. Error: #{ex.message}")
|
34
|
+
end
|
31
35
|
end
|
32
36
|
opts.on('--absolute', "Absolute path, value can be used to prevent automatic using the automatic /api/ path prefix to the path by default.") do
|
33
37
|
options[:absolute_path] = true
|
@@ -350,7 +350,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
350
350
|
if cost_rows.sum { |it| it[:extra].to_f } == 0
|
351
351
|
cost_columns.delete("Extra".upcase)
|
352
352
|
end
|
353
|
-
print as_pretty_table(cost_rows, cost_columns, options)
|
353
|
+
print as_pretty_table(cost_rows, cost_columns, options.merge(include_fields: nil))
|
354
354
|
else
|
355
355
|
print "\n"
|
356
356
|
print yellow, "No invoice totals data", reset, "\n"
|
@@ -641,7 +641,7 @@ Update an invoice.
|
|
641
641
|
payload[:date] = val.to_s
|
642
642
|
end
|
643
643
|
opts.on( '--rebuild', "Rebuild invoices for period. Only applies to mode=costing." ) do |val|
|
644
|
-
|
644
|
+
payload[:rebuild] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
645
645
|
end
|
646
646
|
build_standard_update_options(opts, options, [:query, :auto_confirm])
|
647
647
|
opts.footer = <<-EOT
|
@@ -24,6 +24,9 @@ class Morpheus::Cli::LoadBalancers
|
|
24
24
|
render_response(json_response, options, rest_object_key) do
|
25
25
|
record = json_response[rest_object_key]
|
26
26
|
options[:exclude_username] = record['username'].nil?
|
27
|
+
options[:exclude_owner] = record['owner'].nil?
|
28
|
+
options[:exclude_tenants] = record['tenants'].nil? || record['tenants'].empty?
|
29
|
+
options[:exclude_permissions] = record['resourcePermission'].nil?
|
27
30
|
print_h1 rest_label, [], options
|
28
31
|
print cyan
|
29
32
|
print_description_list(rest_column_definitions(options), record, options)
|
@@ -125,6 +128,8 @@ EOT
|
|
125
128
|
"Provider ID" => 'externalId'
|
126
129
|
}
|
127
130
|
columns.merge!({"Username" => 'username'}) if !options[:exclude_username]
|
131
|
+
columns.merge!({"Owner" => lambda { |it| it['owner']['name'] rescue '' }}) if !options[:exclude_owner]
|
132
|
+
columns.merge!({"Tenants" => lambda { |it| it['tenants'].collect {|tenant| tenant['name']}.join(', ') rescue '' }}) if !options[:exclude_tenants]
|
128
133
|
columns.merge({
|
129
134
|
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
130
135
|
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
@@ -7,7 +7,7 @@ class Morpheus::Cli::NetworkServicesCommand
|
|
7
7
|
set_command_name :'network-services'
|
8
8
|
|
9
9
|
# register_subcommands :list, :get, :add, :update, :remove
|
10
|
-
register_subcommands :list
|
10
|
+
register_subcommands :list, :refresh
|
11
11
|
|
12
12
|
# set_default_subcommand :list
|
13
13
|
|
@@ -90,6 +90,43 @@ class Morpheus::Cli::NetworkServicesCommand
|
|
90
90
|
exit 1
|
91
91
|
end
|
92
92
|
end
|
93
|
+
|
94
|
+
def refresh(args)
|
95
|
+
options = {}
|
96
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
97
|
+
opts.banner = subcommand_usage("[name]")
|
98
|
+
build_common_options(opts, options, [:auto_confirm, :quiet, :json, :dry_run, :remote])
|
99
|
+
opts.footer = "Refresh a network integration/server.\n" +
|
100
|
+
"[name] is required. This is the name or id of a network server/integration."
|
101
|
+
end
|
102
|
+
optparse.parse!(args)
|
103
|
+
if args.count < 1
|
104
|
+
puts optparse
|
105
|
+
exit 1
|
106
|
+
end
|
107
|
+
connect(options)
|
108
|
+
begin
|
109
|
+
server = find_network_server_by_name_or_id(args[0])
|
110
|
+
if !server
|
111
|
+
exit 1
|
112
|
+
end
|
113
|
+
@network_services_interface.setopts(options)
|
114
|
+
if options[:dry_run]
|
115
|
+
print_dry_run @network_services_interface.dry.refresh(server['id'])
|
116
|
+
return
|
117
|
+
end
|
118
|
+
json_response = @network_services_interface.refresh(server['id'])
|
119
|
+
if options[:json]
|
120
|
+
print JSON.pretty_generate(json_response)
|
121
|
+
print "\n"
|
122
|
+
elsif !options[:quiet]
|
123
|
+
print_green_success "Refreshing #{server["name"]}"
|
124
|
+
end
|
125
|
+
rescue RestClient::Exception => e
|
126
|
+
print_rest_exception(e, options)
|
127
|
+
exit 1
|
128
|
+
end
|
129
|
+
end
|
93
130
|
|
94
131
|
|
95
132
|
private
|
@@ -136,4 +173,26 @@ class Morpheus::Cli::NetworkServicesCommand
|
|
136
173
|
end
|
137
174
|
end
|
138
175
|
|
176
|
+
def find_network_server_by_name_or_id(val)
|
177
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
178
|
+
return find_network_server_by_id(val)
|
179
|
+
else
|
180
|
+
return find_network_service_by_name(val)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def find_network_server_by_id(id)
|
185
|
+
begin
|
186
|
+
json_response = @network_services_interface.get_server(id.to_i)
|
187
|
+
return json_response['networkServer']
|
188
|
+
rescue RestClient::Exception => e
|
189
|
+
if e.response && e.response.code == 404
|
190
|
+
print_red_alert "Network Service not found by id #{id}"
|
191
|
+
return nil
|
192
|
+
else
|
193
|
+
raise e
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
139
198
|
end
|
@@ -222,8 +222,10 @@ class Morpheus::Cli::NetworksCommand
|
|
222
222
|
"Domain" => lambda {|it| it['networkDomain'] ? it['networkDomain']['name'] : '' },
|
223
223
|
"Search Domains" => lambda {|it| it['searchDomains'] },
|
224
224
|
"Pool" => lambda {|it| it['pool'] ? it['pool']['name'] : '' },
|
225
|
+
"IPv6 Pool" => lambda {|it| it['poolIPv6'] ? it['poolIPv6']['name'] : '' },
|
225
226
|
"VPC" => lambda {|it| it['zonePool'] ? it['zonePool']['name'] : '' },
|
226
227
|
"DHCP" => lambda {|it| it['dhcpServer'] ? 'Yes' : 'No' },
|
228
|
+
"IPv6 DHCP" => lambda {|it| it['dhcpServerIPv6'] ? 'Yes' : 'No' },
|
227
229
|
"Active" => lambda {|it| format_boolean(it['active']) },
|
228
230
|
"Allow IP Override" => lambda {|it| it['allowStaticOverride'] ? 'Yes' : 'No' },
|
229
231
|
"Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
|
@@ -381,6 +383,12 @@ class Morpheus::Cli::NetworksCommand
|
|
381
383
|
opts.on('--dhcp-server [on|off]', String, "DHCP Server") do |val|
|
382
384
|
options['dhcpServer'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
383
385
|
end
|
386
|
+
opts.on('--dhcp-serverIPv6 [on|off]', String, "IPv6 DHCP Server") do |val|
|
387
|
+
options['dhcpServerIPv6'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
388
|
+
end
|
389
|
+
opts.on('--dhcp-server-ipv6 [on|off]', String, "IPv6 DHCP Server") do |val|
|
390
|
+
options['dhcpServerIPv6'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
391
|
+
end
|
384
392
|
opts.on('--allow-ip-override [on|off]', String, "Allow IP Override") do |val|
|
385
393
|
options['allowStaticOverride'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
386
394
|
end
|
@@ -723,6 +731,15 @@ class Morpheus::Cli::NetworksCommand
|
|
723
731
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'dnsSecondaryIPv6', 'fieldLabel' => 'IPv6 DNS Secondary', 'type' => 'text', 'required' => false, 'description' => ''}], options)
|
724
732
|
payload['network']['dnsSecondaryIPv6'] = v_prompt['dnsSecondaryIPv6']
|
725
733
|
end
|
734
|
+
end
|
735
|
+
# DHCP Server
|
736
|
+
if network_type['dhcpServerEditable'] && payload['network']['dhcpServerIPv6'].nil?
|
737
|
+
if options['dhcpServerIPv6'] != nil
|
738
|
+
payload['network']['dhcpServerIPv6'] = options['dhcpServerIPv6']
|
739
|
+
else
|
740
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'dhcpServerIPv6', 'fieldLabel' => 'IPv6 DHCP Server', 'type' => 'checkbox', 'required' => false, 'description' => ''}], options)
|
741
|
+
payload['network']['dhcpServerIPv6'] = v_prompt['dhcpServerIPv6']
|
742
|
+
end
|
726
743
|
end
|
727
744
|
end
|
728
745
|
|
@@ -1003,6 +1020,12 @@ class Morpheus::Cli::NetworksCommand
|
|
1003
1020
|
opts.on('--dhcp-server [on|off]', String, "DHCP Server") do |val|
|
1004
1021
|
options['dhcpServer'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
1005
1022
|
end
|
1023
|
+
opts.on('--dhcp-serverIPv6 [on|off]', String, "IPv6 DHCP Server") do |val|
|
1024
|
+
options['dhcpServerIPv6'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
1025
|
+
end
|
1026
|
+
opts.on('--dhcp-server-ipv6 [on|off]', String, "IPv6 DHCP Server") do |val|
|
1027
|
+
options['dhcpServerIPv6'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
1028
|
+
end
|
1006
1029
|
opts.on('--allow-ip-override [on|off]', String, "Allow IP Override") do |val|
|
1007
1030
|
options['allowStaticOverride'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
1008
1031
|
end
|
@@ -1244,6 +1267,14 @@ class Morpheus::Cli::NetworksCommand
|
|
1244
1267
|
# payload['network']['dhcpServer'] = v_prompt['dhcpServer']
|
1245
1268
|
end
|
1246
1269
|
|
1270
|
+
# IPv6 DHCP Server
|
1271
|
+
if options['dhcpServerIPv6'] != nil
|
1272
|
+
payload['network']['dhcpServerIPv6'] = options['dhcpServerIPv6']
|
1273
|
+
else
|
1274
|
+
# v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'dhcpServerIPv6', 'fieldLabel' => 'IPv6 DHCP Server', 'type' => 'checkbox', 'required' => false, 'description' => ''}], options)
|
1275
|
+
# payload['network']['dhcpServerIPv6'] = v_prompt['dhcpServerIPv6']
|
1276
|
+
end
|
1277
|
+
|
1247
1278
|
# Allow IP Override
|
1248
1279
|
if options['allowStaticOverride'] != nil
|
1249
1280
|
payload['network']['allowStaticOverride'] = options['allowStaticOverride']
|
@@ -544,13 +544,12 @@ EOT
|
|
544
544
|
def update_cart(args)
|
545
545
|
options = {}
|
546
546
|
params = {}
|
547
|
-
payload = {}
|
548
547
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
549
548
|
opts.banner = subcommand_usage("--name [name]")
|
550
549
|
opts.on('--name [NAME]', String, "Set an optional name for your catalog order") do |val|
|
551
550
|
options[:options]['name'] = val.to_s
|
552
551
|
end
|
553
|
-
build_standard_update_options(opts, options, [:sigdig])
|
552
|
+
build_standard_update_options(opts, options, [:payloads, :sigdig])
|
554
553
|
opts.footer = <<-EOT
|
555
554
|
Update your cart settings, such as name.
|
556
555
|
EOT
|
@@ -560,29 +559,26 @@ EOT
|
|
560
559
|
connect(options)
|
561
560
|
# fetch current cart
|
562
561
|
# cart = @service_catalog_interface.get_cart()['cart']
|
563
|
-
payload = {}
|
564
562
|
update_cart_object_key = 'order'
|
565
|
-
|
566
|
-
payload = options[:payload]
|
563
|
+
payloads = parse_payloads(options, update_cart_object_key) do |payload|
|
567
564
|
payload.deep_merge!({update_cart_object_key => parse_passed_options(options)})
|
568
|
-
else
|
569
|
-
payload.deep_merge!({update_cart_object_key => parse_passed_options(options)})
|
570
|
-
payload.deep_merge!({update_cart_object_key => params})
|
571
565
|
if payload[update_cart_object_key].empty? # || options[:no_prompt]
|
572
566
|
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
573
567
|
end
|
574
568
|
end
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
569
|
+
process_payloads(payloads, options) do |payload|
|
570
|
+
@service_catalog_interface.setopts(options)
|
571
|
+
if options[:dry_run]
|
572
|
+
print_dry_run @service_catalog_interface.dry.update_cart(payload)
|
573
|
+
next
|
574
|
+
end
|
575
|
+
json_response = @service_catalog_interface.update_cart(payload)
|
576
|
+
#cart = json_response['cart']
|
577
|
+
#cart = @service_catalog_interface.get_cart()['cart']
|
578
|
+
render_response(json_response, options, 'cart') do
|
579
|
+
print_green_success "Updated cart"
|
580
|
+
get_cart([] + (options[:remote] ? ["-r",options[:remote]] : []))
|
581
|
+
end
|
586
582
|
end
|
587
583
|
return 0, nil
|
588
584
|
end
|
@@ -590,7 +586,6 @@ EOT
|
|
590
586
|
def add(args)
|
591
587
|
options = {}
|
592
588
|
params = {}
|
593
|
-
payload = {}
|
594
589
|
type_id = nil
|
595
590
|
workflow_context = nil
|
596
591
|
workflow_target = nil
|
@@ -613,7 +608,7 @@ EOT
|
|
613
608
|
workflow_target = val.to_s
|
614
609
|
end
|
615
610
|
opts.add_hidden_option('--sigdig')
|
616
|
-
build_standard_update_options(opts, options, [:sigdig])
|
611
|
+
build_standard_update_options(opts, options, [:payloads, :sigdig])
|
617
612
|
opts.footer = <<-EOT
|
618
613
|
Add an item to your cart
|
619
614
|
[type] is required, this is name or id of a catalog item type.
|
@@ -626,13 +621,8 @@ EOT
|
|
626
621
|
if args.count > 0
|
627
622
|
type_id = args.join(" ")
|
628
623
|
end
|
629
|
-
payload = {}
|
630
624
|
add_item_object_key = 'item'
|
631
|
-
|
632
|
-
if options[:payload]
|
633
|
-
payload = options[:payload]
|
634
|
-
payload.deep_merge!({add_item_object_key => parse_passed_options(options)})
|
635
|
-
else
|
625
|
+
payloads = parse_payloads(options, add_item_object_key) do |payload|
|
636
626
|
payload.deep_merge!({add_item_object_key => parse_passed_options(options)})
|
637
627
|
# prompt for Type
|
638
628
|
if type_id
|
@@ -725,49 +715,45 @@ EOT
|
|
725
715
|
if options[:validate_only]
|
726
716
|
params['validate'] = true
|
727
717
|
end
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
if
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
718
|
+
process_payloads(payloads, options) do |payload|
|
719
|
+
@service_catalog_interface.setopts(options)
|
720
|
+
if options[:dry_run]
|
721
|
+
print_dry_run @service_catalog_interface.dry.create_cart_item(payload, params)
|
722
|
+
next
|
723
|
+
end
|
724
|
+
json_response = @service_catalog_interface.create_cart_item(payload, params)
|
725
|
+
cart_item = json_response['item']
|
726
|
+
render_response(json_response, options) do
|
727
|
+
if options[:validate_only]
|
728
|
+
if json_response['success']
|
729
|
+
print_h2 "Validated Cart Item", [], options
|
730
|
+
cart_item_columns = {
|
731
|
+
"Type" => lambda {|it| it['type']['name'] rescue '' },
|
732
|
+
#"Qty" => lambda {|it| it['quantity'] },
|
733
|
+
"Price" => lambda {|it| it['price'] ? format_money(it['price'] , it['currency'], {sigdig:options[:sigdig] || default_sigdig}) : "No pricing configured" },
|
734
|
+
"Status" => lambda {|it|
|
735
|
+
status_string = format_catalog_item_status(it)
|
736
|
+
if it['errorMessage'].to_s != ""
|
737
|
+
status_string << " - #{it['errorMessage']}"
|
738
|
+
end
|
739
|
+
status_string
|
740
|
+
},
|
741
|
+
#"Config" => lambda {|it| truncate_string(format_name_values(it['config']), 50) }
|
742
|
+
}
|
743
|
+
print as_pretty_table([cart_item], cart_item_columns.upcase_keys!)
|
744
|
+
print reset, "\n"
|
745
|
+
print_green_success(json_response['msg'] || "Item is valid")
|
746
|
+
print reset, "\n"
|
747
|
+
else
|
748
|
+
# not needed because it will be http 400
|
749
|
+
print_rest_errors(json_response, options)
|
750
|
+
end
|
756
751
|
else
|
757
|
-
|
758
|
-
|
752
|
+
print_green_success "Added item to cart"
|
753
|
+
get_cart([] + (options[:remote] ? ["-r",options[:remote]] : []))
|
759
754
|
end
|
760
|
-
else
|
761
|
-
print_green_success "Added item to cart"
|
762
|
-
get_cart([] + (options[:remote] ? ["-r",options[:remote]] : []))
|
763
755
|
end
|
764
756
|
end
|
765
|
-
if json_response['success']
|
766
|
-
return 0, nil
|
767
|
-
else
|
768
|
-
# not needed because it will be http 400
|
769
|
-
return 1, json_response['msg'] || 'request failed'
|
770
|
-
end
|
771
757
|
end
|
772
758
|
|
773
759
|
def update_cart_item(args)
|
@@ -882,7 +868,6 @@ EOT
|
|
882
868
|
def checkout(args)
|
883
869
|
options = {}
|
884
870
|
params = {}
|
885
|
-
payload = {}
|
886
871
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
887
872
|
opts.banner = subcommand_usage()
|
888
873
|
build_standard_add_options(opts, options, [:auto_confirm, :sigdig])
|
@@ -936,7 +921,6 @@ EOT
|
|
936
921
|
def add_order(args)
|
937
922
|
options = {}
|
938
923
|
params = {}
|
939
|
-
payload = {}
|
940
924
|
type_id = nil
|
941
925
|
workflow_context = nil
|
942
926
|
workflow_target = nil
|
@@ -961,7 +945,7 @@ EOT
|
|
961
945
|
opts.on('--target ID', String, "Target Resource (Instance or Server) for operational workflow types") do |val|
|
962
946
|
workflow_target = val.to_s
|
963
947
|
end
|
964
|
-
build_standard_add_options(opts, options, [:sigdig])
|
948
|
+
build_standard_add_options(opts, options, [:payloads, :sigdig])
|
965
949
|
opts.footer = <<-EOT
|
966
950
|
Place an order for new inventory.
|
967
951
|
This allows creating a new order without using the cart.
|
@@ -978,14 +962,8 @@ EOT
|
|
978
962
|
end
|
979
963
|
payload = {}
|
980
964
|
order_object_key = 'order'
|
981
|
-
|
982
|
-
|
983
|
-
if options[:payload]
|
984
|
-
payload = options[:payload]
|
985
|
-
payload.deep_merge!({order_object_key => passed_options}) unless passed_options.empty?
|
986
|
-
else
|
987
|
-
payload.deep_merge!({order_object_key => passed_options}) unless passed_options.empty?
|
988
|
-
|
965
|
+
payloads = parse_payloads(options, order_object_key) do |payload|
|
966
|
+
payload.deep_merge!({order_object_key => {}})
|
989
967
|
# Prompt for 1-N Types
|
990
968
|
# still_prompting = options[:no_prompt] != true
|
991
969
|
still_prompting = true
|
@@ -1106,36 +1084,32 @@ EOT
|
|
1106
1084
|
params['validate'] = true
|
1107
1085
|
#payload['validate'] = true
|
1108
1086
|
end
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
if
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1087
|
+
process_payloads(payloads, options) do |payload|
|
1088
|
+
@service_catalog_interface.setopts(options)
|
1089
|
+
if options[:dry_run]
|
1090
|
+
print_dry_run @service_catalog_interface.dry.create_order(payload, params)
|
1091
|
+
next
|
1092
|
+
end
|
1093
|
+
json_response = @service_catalog_interface.create_order(payload, params)
|
1094
|
+
order = json_response['order'] || json_response['cart']
|
1095
|
+
render_response(json_response, options) do
|
1096
|
+
if options[:validate_only]
|
1097
|
+
if json_response['success']
|
1098
|
+
print_h2 "Review Order", [], options
|
1099
|
+
print_order_details(order, options)
|
1100
|
+
print_green_success(json_response['msg'] || "Order is valid")
|
1101
|
+
print reset, "\n"
|
1102
|
+
else
|
1103
|
+
# not needed because it will be http 400
|
1104
|
+
print_rest_errors(json_response, options)
|
1105
|
+
end
|
1123
1106
|
else
|
1124
|
-
|
1125
|
-
|
1107
|
+
print_green_success "Order placed"
|
1108
|
+
print_h2 "Order Details", [], options
|
1109
|
+
print_order_details(order, options)
|
1126
1110
|
end
|
1127
|
-
else
|
1128
|
-
print_green_success "Order placed"
|
1129
|
-
print_h2 "Order Details", [], options
|
1130
|
-
print_order_details(order, options)
|
1131
1111
|
end
|
1132
1112
|
end
|
1133
|
-
if json_response['success']
|
1134
|
-
return 0, nil
|
1135
|
-
else
|
1136
|
-
# not needed because it will be http 400
|
1137
|
-
return 1, json_response['msg'] || 'request failed'
|
1138
|
-
end
|
1139
1113
|
end
|
1140
1114
|
|
1141
1115
|
def remove(args)
|
@@ -326,6 +326,18 @@ class Morpheus::Cli::ServicePlanCommand
|
|
326
326
|
opts.on('--max-cores NUMBER', String, "Max cores") do |val|
|
327
327
|
((params['config'] ||= {})['ranges'] ||= {})['maxCores'] = val.to_i
|
328
328
|
end
|
329
|
+
opts.on('--min-sockets NUMBER', String, "Min sockets") do |val|
|
330
|
+
((params['config'] ||= {})['ranges'] ||= {})['minSockets'] = val.to_i
|
331
|
+
end
|
332
|
+
opts.on('--max-sockets NUMBER', String, "Max sockets") do |val|
|
333
|
+
((params['config'] ||= {})['ranges'] ||= {})['maxSockets'] = val.to_i
|
334
|
+
end
|
335
|
+
opts.on('--min-cores-per-socket NUMBER', String, "Min cores per socket") do |val|
|
336
|
+
((params['config'] ||= {})['ranges'] ||= {})['minCoresPerSocket'] = val.to_i
|
337
|
+
end
|
338
|
+
opts.on('--max-cores-per-socket NUMBER', String, "Max cores per socket") do |val|
|
339
|
+
((params['config'] ||= {})['ranges'] ||= {})['maxCoresPerSocket'] = val.to_i
|
340
|
+
end
|
329
341
|
add_perms_options(opts, options, ['plans', 'groupDefaults'])
|
330
342
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
331
343
|
opts.footer = "Create service plan"
|
@@ -429,6 +441,16 @@ class Morpheus::Cli::ServicePlanCommand
|
|
429
441
|
{'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxCores', 'fieldLabel' => 'Max Cores', 'type' => 'number', 'displayOrder' => 8}
|
430
442
|
]
|
431
443
|
|
444
|
+
if provision_type['hasSocketRange']
|
445
|
+
addn_options.push({'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minSockets', 'fieldLabel' => 'Min Sockets', 'type' => 'number', 'displayOrder' => 9})
|
446
|
+
addn_options.push({'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxSockets', 'fieldLabel' => 'Max Sockets', 'type' => 'number', 'displayOrder' => 10})
|
447
|
+
end
|
448
|
+
|
449
|
+
if provision_type['hasCoresPerSocketRange']
|
450
|
+
addn_options.push({'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minCoresPerSocket', 'fieldLabel' => 'Min Cores Per Socket', 'type' => 'number', 'displayOrder' => 11})
|
451
|
+
addn_options.push({'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxCoresPerSocket', 'fieldLabel' => 'Max Cores Per Socket', 'type' => 'number', 'displayOrder' => 12})
|
452
|
+
end
|
453
|
+
|
432
454
|
v_prompt = Morpheus::Cli::OptionTypes.prompt(addn_options, options[:options], @api_client, params)
|
433
455
|
params.deep_merge!(v_prompt)
|
434
456
|
|
@@ -575,6 +597,18 @@ class Morpheus::Cli::ServicePlanCommand
|
|
575
597
|
end
|
576
598
|
opts.on('--max-cores NUMBER', String, "Max cores") do |val|
|
577
599
|
((params['config'] ||= {})['ranges'] ||= {})['maxCores'] = val.to_i
|
600
|
+
end
|
601
|
+
opts.on('--min-sockets NUMBER', String, "Min sockets") do |val|
|
602
|
+
((params['config'] ||= {})['ranges'] ||= {})['minSockets'] = val.to_i
|
603
|
+
end
|
604
|
+
opts.on('--max-sockets NUMBER', String, "Max sockets") do |val|
|
605
|
+
((params['config'] ||= {})['ranges'] ||= {})['maxSockets'] = val.to_i
|
606
|
+
end
|
607
|
+
opts.on('--min-cores-per-socket NUMBER', String, "Min cores per socket") do |val|
|
608
|
+
((params['config'] ||= {})['ranges'] ||= {})['minCoresPerSocket'] = val.to_i
|
609
|
+
end
|
610
|
+
opts.on('--max-cores-per-socket NUMBER', String, "Max cores per socket") do |val|
|
611
|
+
((params['config'] ||= {})['ranges'] ||= {})['maxCoresPerSocket'] = val.to_i
|
578
612
|
end
|
579
613
|
add_perms_options(opts, options, ['plans', 'groupDefaults'])
|
580
614
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
@@ -777,15 +777,21 @@ module Morpheus::Cli::ProvisioningHelper
|
|
777
777
|
option_type_list = option_type_list.reject {|opt| ['resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
|
778
778
|
|
779
779
|
resource_pool_options = options_interface.options_for_source('zonePools', {groupId: group_id, siteId: group_id, zoneId: cloud_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], layoutId: layout["id"]}.merge(service_plan.nil? ? {} : {planId: service_plan["id"]}))['data']
|
780
|
-
|
780
|
+
if options[:resource_pool]
|
781
|
+
resource_pool = resource_pool_options.find {|opt| opt['value'] == options[:resource_pool].to_s || opt['value'] == "pool-#{options[:resource_pool]}"}
|
782
|
+
end
|
781
783
|
pool_required = provision_type["zonePoolRequired"]
|
782
|
-
|
784
|
+
# Should pool_id have the pool-,poolGroup- prefix or not?
|
785
|
+
use_pool_prefix = resource_pool_options.find {|opt| opt['value'].to_s.include?("pool") }
|
783
786
|
if resource_pool
|
784
|
-
pool_id = resource_pool['id']
|
787
|
+
pool_id = resource_pool['id'] # id or value?
|
785
788
|
else
|
786
789
|
if options[:default_resource_pool]
|
787
790
|
default_resource_pool = resource_pool_options.find {|rp| rp['id'] == options[:default_resource_pool]}
|
788
791
|
end
|
792
|
+
if use_pool_prefix && options[:options]['config'] && options[:options]['config']['resourcePoolId'] && !options[:options]['config']['resourcePoolId'].to_s.include?("pool")
|
793
|
+
options[:options]['config']['resourcePoolId'] = "pool-" + options[:options]['config']['resourcePoolId'].to_s
|
794
|
+
end
|
789
795
|
resource_pool_option_type ||= {'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => pool_required, 'skipSingleOption' => true, 'description' => 'Select resource pool.', 'defaultValue' => default_resource_pool ? default_resource_pool['name'] : nil}
|
790
796
|
resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{}, no_prompt, true)
|
791
797
|
resource_pool_prompt.deep_compact!
|
@@ -796,7 +802,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
796
802
|
elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
|
797
803
|
pool_id = resource_pool_prompt[resource_pool_option_type['fieldName']]
|
798
804
|
end
|
799
|
-
resource_pool ||= resource_pool_options.find {|it| it['
|
805
|
+
resource_pool ||= resource_pool_options.find {|it| it['value'].to_s == pool_id.to_s}
|
800
806
|
end
|
801
807
|
end
|
802
808
|
end
|
@@ -582,6 +582,9 @@ module Morpheus
|
|
582
582
|
|
583
583
|
if no_prompt
|
584
584
|
if !value_found
|
585
|
+
if default_value == ""
|
586
|
+
default_value = nil
|
587
|
+
end
|
585
588
|
if !default_value.nil? && !select_options.nil?
|
586
589
|
matched_option = select_options.find {|it| it[value_field].to_s == default_value.to_s }
|
587
590
|
if matched_option.nil?
|
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: 6.0
|
4
|
+
version: 6.1.0
|
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: 2023-
|
14
|
+
date: 2023-04-20 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|