morpheus-cli 8.1.2 → 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.
@@ -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] --name --description")
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