morpheus-cli 6.0.2 → 6.1.1
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/backups_interface.rb +4 -0
- data/lib/morpheus/api/cypher_interface.rb +11 -5
- data/lib/morpheus/api/load_balancer_pool_nodes_interface.rb +8 -0
- data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
- data/lib/morpheus/api/load_balancer_pools_secondary_interface.rb +9 -0
- data/lib/morpheus/api/roles_interface.rb +8 -8
- 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/invoices_command.rb +1 -1
- data/lib/morpheus/cli/commands/load_balancer_pool_nodes.rb +87 -0
- data/lib/morpheus/cli/commands/load_balancer_pools.rb +7 -4
- data/lib/morpheus/cli/commands/networks_command.rb +31 -0
- data/lib/morpheus/cli/commands/roles.rb +403 -586
- data/lib/morpheus/cli/commands/service_catalog_command.rb +77 -103
- data/lib/morpheus/cli/commands/users.rb +46 -2
- data/lib/morpheus/cli/mixins/backups_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +6 -6
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3cf8719e8fbab69a6e0de10b9d2920b0394bb55d4928ba4266b82c9430f53658
|
4
|
+
data.tar.gz: a518e58f7b2adc050e786ca6f29e0f7832787e5c294c495d5599960f7232aacc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30263a5621c68dc42d8bb1093c67c21a27eab9c440bd12848a345ddee7200d45d8744531418dd9b936529405623a7c464d44b957503903ac0fc1e2bb462b431a
|
7
|
+
data.tar.gz: 2bc5e8dc185195a1dc470394d487b1c970c1c8626701803636441ae84a6f138352dcafd07481d39a151c24911a05526dfa8748882ee7b1b48f57c02bba584e54
|
data/Dockerfile
CHANGED
@@ -492,6 +492,10 @@ class Morpheus::APIClient
|
|
492
492
|
Morpheus::LoadBalancerPoolsInterface.new(common_interface_options).setopts(@options)
|
493
493
|
end
|
494
494
|
|
495
|
+
def load_balancer_pools_secondary
|
496
|
+
Morpheus::LoadBalancerPoolsSecondaryInterface.new(common_interface_options).setopts(@options)
|
497
|
+
end
|
498
|
+
|
495
499
|
def load_balancer_profiles
|
496
500
|
Morpheus::LoadBalancerProfilesInterface.new(common_interface_options).setopts(@options)
|
497
501
|
end
|
@@ -500,6 +504,10 @@ class Morpheus::APIClient
|
|
500
504
|
Morpheus::LoadBalancerMonitorsInterface.new(common_interface_options).setopts(@options)
|
501
505
|
end
|
502
506
|
|
507
|
+
def load_balancer_pool_nodes
|
508
|
+
Morpheus::LoadBalancerPoolNodesInterface.new(common_interface_options).setopts(@options)
|
509
|
+
end
|
510
|
+
|
503
511
|
def tasks
|
504
512
|
Morpheus::TasksInterface.new(common_interface_options).setopts(@options)
|
505
513
|
end
|
@@ -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
|
@@ -9,7 +9,7 @@ class Morpheus::CypherInterface < Morpheus::APIClient
|
|
9
9
|
|
10
10
|
def get(item_key, params={})
|
11
11
|
raise "#{self.class}.get() passed a blank item_key!" if item_key.to_s == ''
|
12
|
-
url = "#{@base_url}#{base_path}/#{item_key}"
|
12
|
+
url = "#{@base_url}#{base_path}/#{escape_item_key(item_key)}"
|
13
13
|
headers = { :params => params, :authorization => "Bearer #{@access_token}" }
|
14
14
|
execute({method: :get, url: url, headers: headers})
|
15
15
|
end
|
@@ -17,29 +17,35 @@ class Morpheus::CypherInterface < Morpheus::APIClient
|
|
17
17
|
# list url is the same as get but uses $itemKey/?list=true
|
18
18
|
# method: 'LIST' would be neat though
|
19
19
|
def list(item_key=nil, params={})
|
20
|
-
url = item_key ? "#{@base_url}#{base_path}/#{item_key}" : "#{@base_url}#{base_path}"
|
20
|
+
url = item_key ? "#{@base_url}#{base_path}/#{escape_item_key(item_key)}" : "#{@base_url}#{base_path}"
|
21
21
|
params.merge!({list:'true'}) # ditch this probably
|
22
22
|
headers = { :params => params, :authorization => "Bearer #{@access_token}" }
|
23
23
|
execute({method: :get, url: url, headers: headers})
|
24
24
|
end
|
25
25
|
|
26
26
|
def create(item_key, params={}, payload={})
|
27
|
-
url = "#{@base_url}#{base_path}/#{item_key}"
|
27
|
+
url = "#{@base_url}#{base_path}/#{escape_item_key(item_key)}"
|
28
28
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
29
29
|
execute({method: :post, url: url, headers: headers, payload: payload.to_json})
|
30
30
|
end
|
31
31
|
|
32
32
|
# update is not even needed I don't think, same as POST
|
33
33
|
def update(item_key, params={}, payload={})
|
34
|
-
url = "#{@base_url}#{base_path}/#{item_key}"
|
34
|
+
url = "#{@base_url}#{base_path}/#{escape_item_key(item_key)}"
|
35
35
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
36
36
|
execute({method: :put, url: url, headers: headers, payload: payload.to_json})
|
37
37
|
end
|
38
38
|
|
39
39
|
def destroy(item_key, params={})
|
40
|
-
url = "#{@base_url}#{base_path}/#{item_key}"
|
40
|
+
url = "#{@base_url}#{base_path}/#{escape_item_key(item_key)}"
|
41
41
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
42
42
|
execute({method: :delete, url: url, headers: headers})
|
43
43
|
end
|
44
44
|
|
45
|
+
protected
|
46
|
+
|
47
|
+
def escape_item_key(item_key)
|
48
|
+
item_key.to_s.split("/").collect {|i| CGI::escape(i.to_s) }.join("/")
|
49
|
+
end
|
50
|
+
|
45
51
|
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require 'morpheus/api/
|
1
|
+
require 'morpheus/api/rest_interface'
|
2
2
|
|
3
|
-
class Morpheus::LoadBalancerPoolsInterface < Morpheus::
|
3
|
+
class Morpheus::LoadBalancerPoolsInterface < Morpheus::RestInterface
|
4
4
|
|
5
|
-
def base_path
|
6
|
-
"/api/load-
|
5
|
+
def base_path
|
6
|
+
"/api/load-balancer-pools"
|
7
7
|
end
|
8
8
|
|
9
9
|
end
|
@@ -2,10 +2,10 @@ require 'morpheus/api/api_client'
|
|
2
2
|
|
3
3
|
class Morpheus::RolesInterface < Morpheus::APIClient
|
4
4
|
|
5
|
-
def get(account_id, id)
|
5
|
+
def get(account_id, id, params={})
|
6
6
|
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
7
7
|
url = build_url(account_id, id)
|
8
|
-
headers = { params:
|
8
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
9
9
|
execute(method: :get, url: url, headers: headers)
|
10
10
|
end
|
11
11
|
|
@@ -21,24 +21,24 @@ class Morpheus::RolesInterface < Morpheus::APIClient
|
|
21
21
|
execute(method: :get, url: url, headers: headers)
|
22
22
|
end
|
23
23
|
|
24
|
-
def create(account_id, options)
|
24
|
+
def create(account_id, options, params={})
|
25
25
|
url = build_url(account_id)
|
26
26
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
27
27
|
payload = options
|
28
|
-
execute(method: :post, url: url, headers: headers, payload: payload.to_json)
|
28
|
+
execute(method: :post, url: url, headers: headers, payload: payload.to_json, params: params)
|
29
29
|
end
|
30
30
|
|
31
|
-
def update(account_id, id, options)
|
31
|
+
def update(account_id, id, options, params={})
|
32
32
|
url = build_url(account_id, id)
|
33
33
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
34
34
|
payload = options
|
35
|
-
execute(method: :put, url: url, headers: headers, payload: payload.to_json)
|
35
|
+
execute(method: :put, url: url, headers: headers, payload: payload.to_json, params: params)
|
36
36
|
end
|
37
37
|
|
38
|
-
def destroy(account_id, id)
|
38
|
+
def destroy(account_id, id, params={})
|
39
39
|
url = build_url(account_id, id)
|
40
40
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
41
|
-
execute(method: :delete, url: url, headers: headers)
|
41
|
+
execute(method: :delete, url: url, headers: headers, params: params)
|
42
42
|
end
|
43
43
|
|
44
44
|
def update_permission(account_id, id, options)
|
@@ -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
|
|
@@ -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
|