morpheus-cli 8.1.1.1 → 9.0.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 +2 -2
- data/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/groups_interface.rb +2 -1
- data/lib/morpheus/api/servers_interface.rb +0 -1
- data/lib/morpheus/api/support_bundles_interface.rb +46 -0
- data/lib/morpheus/api/systems_interface.rb +32 -0
- data/lib/morpheus/api/tokens_interface.rb +39 -0
- data/lib/morpheus/cli/cli_command.rb +6 -1
- data/lib/morpheus/cli/commands/clients_command.rb +60 -74
- data/lib/morpheus/cli/commands/clouds.rb +30 -2
- data/lib/morpheus/cli/commands/clusters.rb +5 -0
- data/lib/morpheus/cli/commands/execution_request_command.rb +6 -2
- data/lib/morpheus/cli/commands/groups.rb +46 -23
- data/lib/morpheus/cli/commands/hosts.rb +21 -14
- data/lib/morpheus/cli/commands/instances.rb +32 -6
- data/lib/morpheus/cli/commands/storage_volumes.rb +1 -1
- data/lib/morpheus/cli/commands/support_bundles_command.rb +606 -0
- data/lib/morpheus/cli/commands/systems.rb +606 -2
- data/lib/morpheus/cli/commands/tokens_command.rb +391 -0
- data/lib/morpheus/cli/commands/workflows.rb +16 -3
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +30 -14
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +21 -7
- data/lib/morpheus/cli/version.rb +1 -1
- data/test/api/systems_interface_test.rb +26 -0
- data/test/cli/systems_test.rb +206 -0
- metadata +10 -2
|
@@ -6,7 +6,19 @@ class Morpheus::Cli::Systems
|
|
|
6
6
|
|
|
7
7
|
set_command_name :systems
|
|
8
8
|
set_command_description "View and manage systems."
|
|
9
|
-
register_subcommands :list, :get, :add, :update, :remove, :'add-uninitialized', {:'initialize' => 'exec_initialize'}, {:'validate' => 'exec_validate'}
|
|
9
|
+
register_subcommands :list, :get, :add, :update, :remove, :'add-uninitialized', {:'initialize' => 'exec_initialize'}, {:'validate' => 'exec_validate'},
|
|
10
|
+
{:'list-types' => :list_types},
|
|
11
|
+
{:'list-layouts' => :list_layouts},
|
|
12
|
+
{:'list-available-server-updates' => :list_available_server_updates},
|
|
13
|
+
{:'apply-server-update' => :apply_server_update},
|
|
14
|
+
{:'list-available-storage-updates' => :list_available_storage_updates},
|
|
15
|
+
{:'apply-storage-update' => :apply_storage_update},
|
|
16
|
+
{:'list-available-network-updates' => :list_available_network_updates},
|
|
17
|
+
{:'apply-network-update' => :apply_network_update},
|
|
18
|
+
{:'list-available-network-server-updates' => :list_available_network_server_updates},
|
|
19
|
+
{:'apply-network-server-update' => :apply_network_server_update},
|
|
20
|
+
{:'list-available-cluster-updates' => :list_available_cluster_updates},
|
|
21
|
+
{:'apply-cluster-update' => :apply_cluster_update}
|
|
10
22
|
|
|
11
23
|
protected
|
|
12
24
|
|
|
@@ -19,6 +31,11 @@ class Morpheus::Cli::Systems
|
|
|
19
31
|
'systems'
|
|
20
32
|
end
|
|
21
33
|
|
|
34
|
+
# Required so find_by_name_or_id(:servers, id) resolves the lowercase 'server' key
|
|
35
|
+
def server_object_key
|
|
36
|
+
'server'
|
|
37
|
+
end
|
|
38
|
+
|
|
22
39
|
def system_list_column_definitions(options)
|
|
23
40
|
{
|
|
24
41
|
"ID" => 'id',
|
|
@@ -52,12 +69,27 @@ class Morpheus::Cli::Systems
|
|
|
52
69
|
print cyan
|
|
53
70
|
print_description_list(rest_column_definitions(options), record, options)
|
|
54
71
|
print reset,"\n"
|
|
72
|
+
components = record['components'] || []
|
|
73
|
+
if components.any?
|
|
74
|
+
print_h2 "Components (#{components.size})", options
|
|
75
|
+
component_rows = components.collect do |component|
|
|
76
|
+
{
|
|
77
|
+
id: component['id'],
|
|
78
|
+
name: component['name'],
|
|
79
|
+
type_code: component.dig('type', 'code'),
|
|
80
|
+
type_name: component.dig('type', 'name'),
|
|
81
|
+
external_id: component['externalId']
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
print as_pretty_table(component_rows, [:id, :name, :type_code, :type_name, :external_id], options)
|
|
85
|
+
end
|
|
55
86
|
end
|
|
56
87
|
end
|
|
57
88
|
|
|
58
89
|
def add(args)
|
|
59
90
|
options = {}
|
|
60
91
|
params = {}
|
|
92
|
+
components = []
|
|
61
93
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
62
94
|
opts.banner = subcommand_usage("[name]")
|
|
63
95
|
opts.on('--name NAME', String, "System Name") do |val|
|
|
@@ -72,6 +104,12 @@ class Morpheus::Cli::Systems
|
|
|
72
104
|
opts.on('--layout LAYOUT', String, "System Layout ID or name") do |val|
|
|
73
105
|
params['layout'] = val
|
|
74
106
|
end
|
|
107
|
+
opts.on('--component JSON', String, "Component JSON (can be repeated). e.g. '{\"typeCode\":\"compute-node\",\"name\":\"CN-1\"}'") do |val|
|
|
108
|
+
components << JSON.parse(val)
|
|
109
|
+
end
|
|
110
|
+
opts.on('--components JSON', String, "Components JSON array") do |val|
|
|
111
|
+
components.concat(JSON.parse(val))
|
|
112
|
+
end
|
|
75
113
|
build_standard_add_options(opts, options)
|
|
76
114
|
opts.footer = "Create a new system.\n[name] is optional and can be passed as the first argument."
|
|
77
115
|
end
|
|
@@ -84,6 +122,7 @@ class Morpheus::Cli::Systems
|
|
|
84
122
|
payload[rest_object_key] ||= {}
|
|
85
123
|
payload[rest_object_key].deep_merge!(params) unless params.empty?
|
|
86
124
|
payload[rest_object_key]['name'] ||= args[0] if args[0]
|
|
125
|
+
payload[rest_object_key]['components'] = components unless components.empty?
|
|
87
126
|
else
|
|
88
127
|
system_payload = {}
|
|
89
128
|
|
|
@@ -145,6 +184,7 @@ class Morpheus::Cli::Systems
|
|
|
145
184
|
end
|
|
146
185
|
end
|
|
147
186
|
|
|
187
|
+
system_payload['components'] = components unless components.empty?
|
|
148
188
|
payload = {rest_object_key => system_payload}
|
|
149
189
|
end
|
|
150
190
|
|
|
@@ -535,17 +575,38 @@ EOT
|
|
|
535
575
|
def update(args)
|
|
536
576
|
options = {}
|
|
537
577
|
params = {}
|
|
578
|
+
components = []
|
|
579
|
+
components_specified = false
|
|
538
580
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
539
|
-
opts.banner = subcommand_usage("[system]
|
|
581
|
+
opts.banner = subcommand_usage("[system]")
|
|
540
582
|
opts.on("--name NAME", String, "Updates System Name") do |val|
|
|
541
583
|
params['name'] = val.to_s
|
|
542
584
|
end
|
|
543
585
|
opts.on("--description [TEXT]", String, "Updates System Description") do |val|
|
|
544
586
|
params['description'] = val.to_s
|
|
545
587
|
end
|
|
588
|
+
opts.on('--enabled [on|off]', String, "Set whether the system is enabled") do |val|
|
|
589
|
+
params['enabled'] = val.to_s
|
|
590
|
+
end
|
|
591
|
+
opts.on('--externalId ID', String, "Set the external ID") do |val|
|
|
592
|
+
params['externalId'] = val.to_s
|
|
593
|
+
end
|
|
594
|
+
opts.on('--config JSON', String, "Set config JSON") do |val|
|
|
595
|
+
params['config'] = JSON.parse(val)
|
|
596
|
+
end
|
|
597
|
+
opts.on('--component JSON', String, "Component JSON (can be repeated). Pass the full desired component set when using component updates.") do |val|
|
|
598
|
+
components_specified = true
|
|
599
|
+
components << JSON.parse(val)
|
|
600
|
+
end
|
|
601
|
+
opts.on('--components JSON', String, "Components JSON array. This should be the full desired final component list.") do |val|
|
|
602
|
+
components_specified = true
|
|
603
|
+
components.concat(JSON.parse(val))
|
|
604
|
+
end
|
|
546
605
|
build_standard_update_options(opts, options, [:find_by_name])
|
|
547
606
|
opts.footer = <<-EOT
|
|
548
607
|
Update an existing system.
|
|
608
|
+
If system.components is supplied, it is authoritative: omitted components will be removed.
|
|
609
|
+
Omit the components key entirely to leave components unchanged.
|
|
549
610
|
[system] is required. This is the name or id of a system.
|
|
550
611
|
EOT
|
|
551
612
|
end
|
|
@@ -567,6 +628,9 @@ EOT
|
|
|
567
628
|
params.booleanize!
|
|
568
629
|
|
|
569
630
|
payload = parse_payload(options) || {rest_object_key => params}
|
|
631
|
+
payload[rest_object_key] ||= {}
|
|
632
|
+
payload[rest_object_key].deep_merge!(params) unless params.empty?
|
|
633
|
+
payload[rest_object_key]['components'] = components if components_specified
|
|
570
634
|
if payload[rest_object_key].nil? || payload[rest_object_key].empty?
|
|
571
635
|
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
|
572
636
|
end
|
|
@@ -596,4 +660,544 @@ EOT
|
|
|
596
660
|
items = result ? (result['systemTypeLayouts'] || result[:systemTypeLayouts] || result['layouts'] || result[:layouts] || []) : []
|
|
597
661
|
items.map { |l| {'id' => l['id'] || l[:id], 'name' => l['name'] || l[:name], 'value' => (l['id'] || l[:id]).to_s, 'code' => l['code'] || l[:code], 'componentTypes' => l['componentTypes'] || l[:componentTypes] || []} }
|
|
598
662
|
end
|
|
663
|
+
|
|
664
|
+
def list_types(args)
|
|
665
|
+
options = {}
|
|
666
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
667
|
+
opts.banner = subcommand_usage()
|
|
668
|
+
build_standard_list_options(opts, options)
|
|
669
|
+
opts.footer = "List available system types."
|
|
670
|
+
end
|
|
671
|
+
optparse.parse!(args)
|
|
672
|
+
connect(options)
|
|
673
|
+
begin
|
|
674
|
+
params = {}
|
|
675
|
+
params.merge!(parse_list_options(options))
|
|
676
|
+
@system_types_interface = @api_client.system_types
|
|
677
|
+
@system_types_interface.setopts(options)
|
|
678
|
+
if options[:dry_run]
|
|
679
|
+
print_dry_run @system_types_interface.dry.list(params)
|
|
680
|
+
return 0
|
|
681
|
+
end
|
|
682
|
+
json_response = @system_types_interface.list(params)
|
|
683
|
+
system_types = json_response['systemTypes'] || []
|
|
684
|
+
render_response(json_response, options, 'systemTypes') do
|
|
685
|
+
print_h1 "System Types", [], options
|
|
686
|
+
if system_types.empty?
|
|
687
|
+
print cyan, "No system types found.", reset, "\n"
|
|
688
|
+
else
|
|
689
|
+
columns = {
|
|
690
|
+
"ID" => 'id',
|
|
691
|
+
"Name" => 'name',
|
|
692
|
+
"Code" => 'code',
|
|
693
|
+
}
|
|
694
|
+
print cyan
|
|
695
|
+
print as_pretty_table(system_types, columns.upcase_keys!, options)
|
|
696
|
+
print_results_pagination({size: system_types.size, total: (json_response['meta'] ? json_response['meta']['total'] : system_types.size)})
|
|
697
|
+
end
|
|
698
|
+
print reset, "\n"
|
|
699
|
+
end
|
|
700
|
+
return 0
|
|
701
|
+
rescue RestClient::Exception => e
|
|
702
|
+
print_rest_exception(e, options)
|
|
703
|
+
exit 1
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def list_layouts(args)
|
|
708
|
+
options = {}
|
|
709
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
710
|
+
opts.banner = subcommand_usage("[type]")
|
|
711
|
+
build_standard_list_options(opts, options)
|
|
712
|
+
opts.footer = <<-EOT
|
|
713
|
+
List available layouts for a system type.
|
|
714
|
+
[type] is required. This is the id of a system type.
|
|
715
|
+
Use 'systems list-types' to find the type id.
|
|
716
|
+
EOT
|
|
717
|
+
end
|
|
718
|
+
optparse.parse!(args)
|
|
719
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
|
720
|
+
connect(options)
|
|
721
|
+
begin
|
|
722
|
+
type_id = args[0].to_i
|
|
723
|
+
params = {}
|
|
724
|
+
params.merge!(parse_list_options(options))
|
|
725
|
+
@system_types_interface = @api_client.system_types
|
|
726
|
+
@system_types_interface.setopts(options)
|
|
727
|
+
if options[:dry_run]
|
|
728
|
+
print_dry_run @system_types_interface.dry.list_layouts(type_id, params)
|
|
729
|
+
return 0
|
|
730
|
+
end
|
|
731
|
+
json_response = @system_types_interface.list_layouts(type_id, params)
|
|
732
|
+
layouts = json_response['systemTypeLayouts'] || []
|
|
733
|
+
render_response(json_response, options, 'systemTypeLayouts') do
|
|
734
|
+
print_h1 "System Type Layouts", [], options
|
|
735
|
+
if layouts.empty?
|
|
736
|
+
print cyan, "No layouts found.", reset, "\n"
|
|
737
|
+
else
|
|
738
|
+
columns = {
|
|
739
|
+
"ID" => 'id',
|
|
740
|
+
"Name" => 'name',
|
|
741
|
+
"Code" => 'code',
|
|
742
|
+
}
|
|
743
|
+
print cyan
|
|
744
|
+
print as_pretty_table(layouts, columns.upcase_keys!, options)
|
|
745
|
+
print_results_pagination({size: layouts.size, total: (json_response['meta'] ? json_response['meta']['total'] : layouts.size)})
|
|
746
|
+
layouts.each do |layout|
|
|
747
|
+
component_types = layout['componentTypes'] || []
|
|
748
|
+
next if component_types.empty?
|
|
749
|
+
print_h2 "Components for #{layout['name']}", options
|
|
750
|
+
component_type_rows = component_types.collect do |ct|
|
|
751
|
+
{
|
|
752
|
+
id: ct['id'],
|
|
753
|
+
code: ct['code'],
|
|
754
|
+
name: ct['name'],
|
|
755
|
+
category: ct['category']
|
|
756
|
+
}
|
|
757
|
+
end
|
|
758
|
+
print as_pretty_table(component_type_rows, [:id, :code, :name, :category], options)
|
|
759
|
+
end
|
|
760
|
+
end
|
|
761
|
+
print reset, "\n"
|
|
762
|
+
end
|
|
763
|
+
return 0
|
|
764
|
+
rescue RestClient::Exception => e
|
|
765
|
+
print_rest_exception(e, options)
|
|
766
|
+
exit 1
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def list_available_server_updates(args)
|
|
771
|
+
options = {}
|
|
772
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
773
|
+
opts.banner = subcommand_usage("[system] [server]")
|
|
774
|
+
build_standard_list_options(opts, options)
|
|
775
|
+
opts.footer = <<-EOT
|
|
776
|
+
List available update definitions for a compute server component of a system.
|
|
777
|
+
[system] is required. This is the name or id of a system.
|
|
778
|
+
[server] is required. This is the name or id of the compute server.
|
|
779
|
+
EOT
|
|
780
|
+
end
|
|
781
|
+
optparse.parse!(args)
|
|
782
|
+
verify_args!(args:args, optparse:optparse, count:2)
|
|
783
|
+
connect(options)
|
|
784
|
+
begin
|
|
785
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
786
|
+
return 1 if system.nil?
|
|
787
|
+
server = find_by_name_or_id(:servers, args[1])
|
|
788
|
+
return 1 if server.nil?
|
|
789
|
+
params = {}
|
|
790
|
+
params.merge!(parse_list_options(options))
|
|
791
|
+
@systems_interface.setopts(options)
|
|
792
|
+
if options[:dry_run]
|
|
793
|
+
print_dry_run @systems_interface.dry.list_compute_server_update_definitions(system['id'], server['id'], params)
|
|
794
|
+
return
|
|
795
|
+
end
|
|
796
|
+
json_response = @systems_interface.list_compute_server_update_definitions(system['id'], server['id'], params)
|
|
797
|
+
update_definitions = json_response['updateDefinitions']
|
|
798
|
+
render_response(json_response, options, 'updateDefinitions') do
|
|
799
|
+
print_h1 "Available Server Updates: #{system['name']} / #{server['name']}", [], options
|
|
800
|
+
if update_definitions.nil? || update_definitions.empty?
|
|
801
|
+
print cyan, "No update definitions found.", reset, "\n"
|
|
802
|
+
else
|
|
803
|
+
columns = {
|
|
804
|
+
"ID" => 'id',
|
|
805
|
+
"Name" => 'name',
|
|
806
|
+
"Version" => 'updateVersion',
|
|
807
|
+
"Severity" => 'severity',
|
|
808
|
+
"Type" => 'type',
|
|
809
|
+
"Reboot" => lambda {|it| format_boolean(it['requiresReboot']) },
|
|
810
|
+
"Rollback" => lambda {|it| format_boolean(it['supportsRollback']) },
|
|
811
|
+
"Released" => lambda {|it| it['updateReleaseDate'] ? format_local_dt(it['updateReleaseDate']) : '' },
|
|
812
|
+
}
|
|
813
|
+
print cyan
|
|
814
|
+
print as_pretty_table(update_definitions, columns.upcase_keys!, options)
|
|
815
|
+
print_results_pagination({size: update_definitions.size, total: (json_response['meta'] ? json_response['meta']['total'] : update_definitions.size)})
|
|
816
|
+
end
|
|
817
|
+
print reset, "\n"
|
|
818
|
+
end
|
|
819
|
+
return 0
|
|
820
|
+
rescue RestClient::Exception => e
|
|
821
|
+
print_rest_exception(e, options)
|
|
822
|
+
exit 1
|
|
823
|
+
end
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
def apply_server_update(args)
|
|
827
|
+
options = {}
|
|
828
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
829
|
+
opts.banner = subcommand_usage("[system] [server] [updateDefinitionId]")
|
|
830
|
+
opts.on('--dry-run-update', "Execute as a dry run — passes dryRun:true to the server, no changes applied.") do
|
|
831
|
+
options[:dry_run_update] = true
|
|
832
|
+
end
|
|
833
|
+
build_standard_update_options(opts, options)
|
|
834
|
+
opts.footer = <<-EOT
|
|
835
|
+
Apply an update definition to a compute server component of a system.
|
|
836
|
+
[system] is required. This is the name or id of a system.
|
|
837
|
+
[server] is required. This is the name or id of the compute server.
|
|
838
|
+
[updateDefinitionId] is required. This is the id of the update definition.
|
|
839
|
+
EOT
|
|
840
|
+
end
|
|
841
|
+
optparse.parse!(args)
|
|
842
|
+
verify_args!(args:args, optparse:optparse, count:3)
|
|
843
|
+
connect(options)
|
|
844
|
+
begin
|
|
845
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
846
|
+
return 1 if system.nil?
|
|
847
|
+
server = find_by_name_or_id(:servers, args[1])
|
|
848
|
+
return 1 if server.nil?
|
|
849
|
+
update_definition_id = args[2]
|
|
850
|
+
payload = {}
|
|
851
|
+
payload['dryRun'] = true if options[:dry_run_update]
|
|
852
|
+
payload.deep_merge!(parse_passed_options(options))
|
|
853
|
+
params = {}
|
|
854
|
+
@systems_interface.setopts(options)
|
|
855
|
+
if options[:dry_run]
|
|
856
|
+
print_dry_run @systems_interface.dry.apply_compute_server_update_definition(system['id'], server['id'], update_definition_id, payload, params)
|
|
857
|
+
return
|
|
858
|
+
end
|
|
859
|
+
json_response = @systems_interface.apply_compute_server_update_definition(system['id'], server['id'], update_definition_id, payload, params)
|
|
860
|
+
render_response(json_response, options) do
|
|
861
|
+
print_green_success "Update operation #{json_response['updateOperation']['id']} queued for server #{server['name']} on system #{system['name']}."
|
|
862
|
+
end
|
|
863
|
+
return 0
|
|
864
|
+
rescue RestClient::Exception => e
|
|
865
|
+
print_rest_exception(e, options)
|
|
866
|
+
exit 1
|
|
867
|
+
end
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
def list_available_storage_updates(args)
|
|
871
|
+
options = {}
|
|
872
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
873
|
+
opts.banner = subcommand_usage("[system] [storage-server]")
|
|
874
|
+
build_standard_list_options(opts, options)
|
|
875
|
+
opts.footer = <<-EOT
|
|
876
|
+
List available update definitions for a storage server component of a system.
|
|
877
|
+
[system] is required. This is the name or id of a system.
|
|
878
|
+
[storage-server] is required. This is the name or id of the storage server.
|
|
879
|
+
EOT
|
|
880
|
+
end
|
|
881
|
+
optparse.parse!(args)
|
|
882
|
+
verify_args!(args:args, optparse:optparse, count:2)
|
|
883
|
+
connect(options)
|
|
884
|
+
begin
|
|
885
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
886
|
+
return 1 if system.nil?
|
|
887
|
+
storage_server = find_by_name_or_id(:storage_servers, args[1])
|
|
888
|
+
return 1 if storage_server.nil?
|
|
889
|
+
params = {}
|
|
890
|
+
params.merge!(parse_list_options(options))
|
|
891
|
+
@systems_interface.setopts(options)
|
|
892
|
+
if options[:dry_run]
|
|
893
|
+
print_dry_run @systems_interface.dry.list_storage_server_update_definitions(system['id'], storage_server['id'], params)
|
|
894
|
+
return
|
|
895
|
+
end
|
|
896
|
+
json_response = @systems_interface.list_storage_server_update_definitions(system['id'], storage_server['id'], params)
|
|
897
|
+
update_definitions = json_response['updateDefinitions']
|
|
898
|
+
render_response(json_response, options, 'updateDefinitions') do
|
|
899
|
+
print_h1 "Available Storage Updates: #{system['name']} / #{storage_server['name']}", [], options
|
|
900
|
+
if update_definitions.nil? || update_definitions.empty?
|
|
901
|
+
print cyan, "No update definitions found.", reset, "\n"
|
|
902
|
+
else
|
|
903
|
+
columns = {
|
|
904
|
+
"ID" => 'id',
|
|
905
|
+
"Name" => 'name',
|
|
906
|
+
"Version" => 'updateVersion',
|
|
907
|
+
"Severity" => 'severity',
|
|
908
|
+
"Type" => 'type',
|
|
909
|
+
"Reboot" => lambda {|it| format_boolean(it['requiresReboot']) },
|
|
910
|
+
"Rollback" => lambda {|it| format_boolean(it['supportsRollback']) },
|
|
911
|
+
"Released" => lambda {|it| it['updateReleaseDate'] ? format_local_dt(it['updateReleaseDate']) : '' },
|
|
912
|
+
}
|
|
913
|
+
print cyan
|
|
914
|
+
print as_pretty_table(update_definitions, columns.upcase_keys!, options)
|
|
915
|
+
print_results_pagination({size: update_definitions.size, total: (json_response['meta'] ? json_response['meta']['total'] : update_definitions.size)})
|
|
916
|
+
end
|
|
917
|
+
print reset, "\n"
|
|
918
|
+
end
|
|
919
|
+
return 0
|
|
920
|
+
rescue RestClient::Exception => e
|
|
921
|
+
print_rest_exception(e, options)
|
|
922
|
+
exit 1
|
|
923
|
+
end
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
def apply_storage_update(args)
|
|
927
|
+
options = {}
|
|
928
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
929
|
+
opts.banner = subcommand_usage("[system] [storage-server] [updateDefinitionId]")
|
|
930
|
+
opts.on('--dry-run-update', "Execute as a dry run — passes dryRun:true to the server, no changes applied.") do
|
|
931
|
+
options[:dry_run_update] = true
|
|
932
|
+
end
|
|
933
|
+
build_standard_update_options(opts, options)
|
|
934
|
+
opts.footer = <<-EOT
|
|
935
|
+
Apply an update definition to a storage server component of a system.
|
|
936
|
+
[system] is required. This is the name or id of a system.
|
|
937
|
+
[storage-server] is required. This is the name or id of the storage server.
|
|
938
|
+
[updateDefinitionId] is required. This is the id of the update definition.
|
|
939
|
+
EOT
|
|
940
|
+
end
|
|
941
|
+
optparse.parse!(args)
|
|
942
|
+
verify_args!(args:args, optparse:optparse, count:3)
|
|
943
|
+
connect(options)
|
|
944
|
+
begin
|
|
945
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
946
|
+
return 1 if system.nil?
|
|
947
|
+
storage_server = find_by_name_or_id(:storage_servers, args[1])
|
|
948
|
+
return 1 if storage_server.nil?
|
|
949
|
+
update_definition_id = args[2]
|
|
950
|
+
payload = {}
|
|
951
|
+
payload['dryRun'] = true if options[:dry_run_update]
|
|
952
|
+
payload.deep_merge!(parse_passed_options(options))
|
|
953
|
+
params = {}
|
|
954
|
+
@systems_interface.setopts(options)
|
|
955
|
+
if options[:dry_run]
|
|
956
|
+
print_dry_run @systems_interface.dry.apply_storage_server_update_definition(system['id'], storage_server['id'], update_definition_id, payload, params)
|
|
957
|
+
return
|
|
958
|
+
end
|
|
959
|
+
json_response = @systems_interface.apply_storage_server_update_definition(system['id'], storage_server['id'], update_definition_id, payload, params)
|
|
960
|
+
render_response(json_response, options) do
|
|
961
|
+
print_green_success "Update operation #{json_response['updateOperation']['id']} queued for storage server #{storage_server['name']} on system #{system['name']}."
|
|
962
|
+
end
|
|
963
|
+
return 0
|
|
964
|
+
rescue RestClient::Exception => e
|
|
965
|
+
print_rest_exception(e, options)
|
|
966
|
+
exit 1
|
|
967
|
+
end
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
def list_available_network_server_updates(args)
|
|
971
|
+
options = {}
|
|
972
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
973
|
+
opts.banner = subcommand_usage("[system] [network-server]")
|
|
974
|
+
build_standard_list_options(opts, options)
|
|
975
|
+
opts.footer = <<-EOT
|
|
976
|
+
List available update definitions for a network server component of a system.
|
|
977
|
+
[system] is required. This is the name or id of a system.
|
|
978
|
+
[network-server] is required. This is the name or id of the network server.
|
|
979
|
+
EOT
|
|
980
|
+
end
|
|
981
|
+
optparse.parse!(args)
|
|
982
|
+
verify_args!(args:args, optparse:optparse, count:2)
|
|
983
|
+
connect(options)
|
|
984
|
+
begin
|
|
985
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
986
|
+
return 1 if system.nil?
|
|
987
|
+
network_server = find_by_name_or_id(:network_servers, args[1])
|
|
988
|
+
return 1 if network_server.nil?
|
|
989
|
+
params = {}
|
|
990
|
+
params.merge!(parse_list_options(options))
|
|
991
|
+
@systems_interface.setopts(options)
|
|
992
|
+
if options[:dry_run]
|
|
993
|
+
print_dry_run @systems_interface.dry.list_network_server_update_definitions(system['id'], network_server['id'], params)
|
|
994
|
+
return
|
|
995
|
+
end
|
|
996
|
+
json_response = @systems_interface.list_network_server_update_definitions(system['id'], network_server['id'], params)
|
|
997
|
+
update_definitions = json_response['updateDefinitions']
|
|
998
|
+
render_response(json_response, options, 'updateDefinitions') do
|
|
999
|
+
print_h1 "Available Network Server Updates: #{system['name']} / #{network_server['name']}", [], options
|
|
1000
|
+
if update_definitions.nil? || update_definitions.empty?
|
|
1001
|
+
print cyan, "No update definitions found.", reset, "\n"
|
|
1002
|
+
else
|
|
1003
|
+
columns = {
|
|
1004
|
+
"ID" => 'id',
|
|
1005
|
+
"Name" => 'name',
|
|
1006
|
+
"Version" => 'updateVersion',
|
|
1007
|
+
"Severity" => 'severity',
|
|
1008
|
+
"Type" => 'type',
|
|
1009
|
+
"Reboot" => lambda {|it| format_boolean(it['requiresReboot']) },
|
|
1010
|
+
"Rollback" => lambda {|it| format_boolean(it['supportsRollback']) },
|
|
1011
|
+
"Released" => lambda {|it| it['updateReleaseDate'] ? format_local_dt(it['updateReleaseDate']) : '' },
|
|
1012
|
+
}
|
|
1013
|
+
print cyan
|
|
1014
|
+
print as_pretty_table(update_definitions, columns.upcase_keys!, options)
|
|
1015
|
+
print_results_pagination({size: update_definitions.size, total: (json_response['meta'] ? json_response['meta']['total'] : update_definitions.size)})
|
|
1016
|
+
end
|
|
1017
|
+
print reset, "\n"
|
|
1018
|
+
end
|
|
1019
|
+
return 0
|
|
1020
|
+
rescue RestClient::Exception => e
|
|
1021
|
+
print_rest_exception(e, options)
|
|
1022
|
+
exit 1
|
|
1023
|
+
end
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
def list_available_network_updates(args)
|
|
1027
|
+
list_available_network_server_updates(args)
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
def apply_network_server_update(args)
|
|
1031
|
+
options = {}
|
|
1032
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
1033
|
+
opts.banner = subcommand_usage("[system] [network-server] [updateDefinitionId]")
|
|
1034
|
+
opts.on('--dry-run-update', "Execute as a dry run — passes dryRun:true to the server, no changes applied.") do
|
|
1035
|
+
options[:dry_run_update] = true
|
|
1036
|
+
end
|
|
1037
|
+
build_standard_update_options(opts, options)
|
|
1038
|
+
opts.footer = <<-EOT
|
|
1039
|
+
Apply an update definition to a network server component of a system.
|
|
1040
|
+
[system] is required. This is the name or id of a system.
|
|
1041
|
+
[network-server] is required. This is the name or id of the network server.
|
|
1042
|
+
[updateDefinitionId] is required. This is the id of the update definition.
|
|
1043
|
+
EOT
|
|
1044
|
+
end
|
|
1045
|
+
optparse.parse!(args)
|
|
1046
|
+
verify_args!(args:args, optparse:optparse, count:3)
|
|
1047
|
+
connect(options)
|
|
1048
|
+
begin
|
|
1049
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
1050
|
+
return 1 if system.nil?
|
|
1051
|
+
network_server = find_by_name_or_id(:network_servers, args[1])
|
|
1052
|
+
return 1 if network_server.nil?
|
|
1053
|
+
update_definition_id = args[2]
|
|
1054
|
+
payload = {}
|
|
1055
|
+
payload['dryRun'] = true if options[:dry_run_update]
|
|
1056
|
+
payload.deep_merge!(parse_passed_options(options))
|
|
1057
|
+
params = {}
|
|
1058
|
+
@systems_interface.setopts(options)
|
|
1059
|
+
if options[:dry_run]
|
|
1060
|
+
print_dry_run @systems_interface.dry.apply_network_server_update_definition(system['id'], network_server['id'], update_definition_id, payload, params)
|
|
1061
|
+
return
|
|
1062
|
+
end
|
|
1063
|
+
json_response = @systems_interface.apply_network_server_update_definition(system['id'], network_server['id'], update_definition_id, payload, params)
|
|
1064
|
+
render_response(json_response, options) do
|
|
1065
|
+
print_green_success "Update operation #{json_response['updateOperation']['id']} queued for network server #{network_server['name']} on system #{system['name']}."
|
|
1066
|
+
end
|
|
1067
|
+
return 0
|
|
1068
|
+
rescue RestClient::Exception => e
|
|
1069
|
+
print_rest_exception(e, options)
|
|
1070
|
+
exit 1
|
|
1071
|
+
end
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
def apply_network_update(args)
|
|
1075
|
+
apply_network_server_update(args)
|
|
1076
|
+
end
|
|
1077
|
+
|
|
1078
|
+
def list_available_cluster_updates(args)
|
|
1079
|
+
options = {}
|
|
1080
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
1081
|
+
opts.banner = subcommand_usage("[system] [cluster]")
|
|
1082
|
+
build_standard_list_options(opts, options)
|
|
1083
|
+
opts.footer = <<-EOT
|
|
1084
|
+
List available update definitions for a cluster component of a system.
|
|
1085
|
+
[system] is required. This is the name or id of a system.
|
|
1086
|
+
[cluster] is required. This is the name or id of the cluster.
|
|
1087
|
+
EOT
|
|
1088
|
+
end
|
|
1089
|
+
optparse.parse!(args)
|
|
1090
|
+
verify_args!(args:args, optparse:optparse, count:2)
|
|
1091
|
+
connect(options)
|
|
1092
|
+
begin
|
|
1093
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
1094
|
+
return 1 if system.nil?
|
|
1095
|
+
cluster = find_cluster_by_name_or_id(args[1])
|
|
1096
|
+
return 1 if cluster.nil?
|
|
1097
|
+
params = {}
|
|
1098
|
+
params.merge!(parse_list_options(options))
|
|
1099
|
+
@systems_interface.setopts(options)
|
|
1100
|
+
if options[:dry_run]
|
|
1101
|
+
print_dry_run @systems_interface.dry.list_cluster_update_definitions(system['id'], cluster['id'], params)
|
|
1102
|
+
return
|
|
1103
|
+
end
|
|
1104
|
+
json_response = @systems_interface.list_cluster_update_definitions(system['id'], cluster['id'], params)
|
|
1105
|
+
update_definitions = json_response['updateDefinitions']
|
|
1106
|
+
render_response(json_response, options, 'updateDefinitions') do
|
|
1107
|
+
print_h1 "Available Cluster Updates: #{system['name']} / #{cluster['name']}", [], options
|
|
1108
|
+
if update_definitions.nil? || update_definitions.empty?
|
|
1109
|
+
print cyan, "No update definitions found.", reset, "\n"
|
|
1110
|
+
else
|
|
1111
|
+
columns = {
|
|
1112
|
+
"ID" => 'id',
|
|
1113
|
+
"Name" => 'name',
|
|
1114
|
+
"Version" => 'updateVersion',
|
|
1115
|
+
"Severity" => 'severity',
|
|
1116
|
+
"Type" => 'type',
|
|
1117
|
+
"Reboot" => lambda {|it| format_boolean(it['requiresReboot']) },
|
|
1118
|
+
"Rollback" => lambda {|it| format_boolean(it['supportsRollback']) },
|
|
1119
|
+
"Released" => lambda {|it| it['updateReleaseDate'] ? format_local_dt(it['updateReleaseDate']) : '' },
|
|
1120
|
+
}
|
|
1121
|
+
print cyan
|
|
1122
|
+
print as_pretty_table(update_definitions, columns.upcase_keys!, options)
|
|
1123
|
+
print_results_pagination({size: update_definitions.size, total: (json_response['meta'] ? json_response['meta']['total'] : update_definitions.size)})
|
|
1124
|
+
end
|
|
1125
|
+
print reset, "\n"
|
|
1126
|
+
end
|
|
1127
|
+
return 0
|
|
1128
|
+
rescue RestClient::Exception => e
|
|
1129
|
+
print_rest_exception(e, options)
|
|
1130
|
+
exit 1
|
|
1131
|
+
end
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
def apply_cluster_update(args)
|
|
1135
|
+
options = {}
|
|
1136
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
1137
|
+
opts.banner = subcommand_usage("[system] [cluster] [updateDefinitionId]")
|
|
1138
|
+
opts.on('--dry-run-update', "Execute as a dry run — passes dryRun:true to the server, no changes applied.") do
|
|
1139
|
+
options[:dry_run_update] = true
|
|
1140
|
+
end
|
|
1141
|
+
build_standard_update_options(opts, options)
|
|
1142
|
+
opts.footer = <<-EOT
|
|
1143
|
+
Apply an update definition to a cluster component of a system.
|
|
1144
|
+
[system] is required. This is the name or id of a system.
|
|
1145
|
+
[cluster] is required. This is the name or id of the cluster.
|
|
1146
|
+
[updateDefinitionId] is required. This is the id of the update definition.
|
|
1147
|
+
EOT
|
|
1148
|
+
end
|
|
1149
|
+
optparse.parse!(args)
|
|
1150
|
+
verify_args!(args:args, optparse:optparse, count:3)
|
|
1151
|
+
connect(options)
|
|
1152
|
+
begin
|
|
1153
|
+
system = find_by_name_or_id(:systems, args[0])
|
|
1154
|
+
return 1 if system.nil?
|
|
1155
|
+
cluster = find_cluster_by_name_or_id(args[1])
|
|
1156
|
+
return 1 if cluster.nil?
|
|
1157
|
+
update_definition_id = args[2]
|
|
1158
|
+
payload = {}
|
|
1159
|
+
payload['dryRun'] = true if options[:dry_run_update]
|
|
1160
|
+
payload.deep_merge!(parse_passed_options(options))
|
|
1161
|
+
params = {}
|
|
1162
|
+
@systems_interface.setopts(options)
|
|
1163
|
+
if options[:dry_run]
|
|
1164
|
+
print_dry_run @systems_interface.dry.apply_cluster_update_definition(system['id'], cluster['id'], update_definition_id, payload, params)
|
|
1165
|
+
return
|
|
1166
|
+
end
|
|
1167
|
+
json_response = @systems_interface.apply_cluster_update_definition(system['id'], cluster['id'], update_definition_id, payload, params)
|
|
1168
|
+
render_response(json_response, options) do
|
|
1169
|
+
print_green_success "Update operation #{json_response['updateOperation']['id']} queued for cluster #{cluster['name']} on system #{system['name']}."
|
|
1170
|
+
end
|
|
1171
|
+
return 0
|
|
1172
|
+
rescue RestClient::Exception => e
|
|
1173
|
+
print_rest_exception(e, options)
|
|
1174
|
+
exit 1
|
|
1175
|
+
end
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
private
|
|
1179
|
+
|
|
1180
|
+
def find_cluster_by_name_or_id(val)
|
|
1181
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
|
1182
|
+
json_result = @api_client.clusters.get(val.to_i)
|
|
1183
|
+
cluster = json_result['cluster']
|
|
1184
|
+
if cluster.nil?
|
|
1185
|
+
print_red_alert "Cluster not found by id #{val}"
|
|
1186
|
+
return nil
|
|
1187
|
+
end
|
|
1188
|
+
cluster
|
|
1189
|
+
else
|
|
1190
|
+
json_result = @api_client.clusters.list({name: val})
|
|
1191
|
+
clusters = json_result['clusters']
|
|
1192
|
+
if clusters.nil? || clusters.empty?
|
|
1193
|
+
print_red_alert "Cluster not found by name '#{val}'"
|
|
1194
|
+
return nil
|
|
1195
|
+
elsif clusters.size > 1
|
|
1196
|
+
print_red_alert "#{clusters.size} clusters found by name '#{val}'. Use the id instead."
|
|
1197
|
+
return nil
|
|
1198
|
+
end
|
|
1199
|
+
clusters.first
|
|
1200
|
+
end
|
|
1201
|
+
end
|
|
1202
|
+
|
|
599
1203
|
end
|