Hello community, here is the log from the commit of package yast2-cluster for openSUSE:Factory checked in at 2018-01-06 18:50:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-cluster (Old) and /work/SRC/openSUSE:Factory/.yast2-cluster.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-cluster" Sat Jan 6 18:50:50 2018 rev:21 rq:561811 version:4.0.2 Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-cluster/yast2-cluster.changes 2017-12-05 01:30:19.469110258 +0100 +++ /work/SRC/openSUSE:Factory/.yast2-cluster.new/yast2-cluster.changes 2018-01-06 18:51:01.086104448 +0100 @@ -1,0 +2,6 @@ +Fri Dec 22 07:04:15 UTC 2017 - nw...@suse.com + +- bsc#1070961, support corosync qdevice. +- Version 4.0.2 + +------------------------------------------------------------------- Old: ---- yast2-cluster-4.0.1.tar.bz2 New: ---- yast2-cluster-4.0.2.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-cluster.spec ++++++ --- /var/tmp/diff_new_pack.JqlsHe/_old 2018-01-06 18:51:01.662077536 +0100 +++ /var/tmp/diff_new_pack.JqlsHe/_new 2018-01-06 18:51:01.666077349 +0100 @@ -1,7 +1,7 @@ # # spec file for package yast2-cluster # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ Name: yast2-cluster %define _fwdefdir /etc/sysconfig/SuSEfirewall2.d/services -Version: 4.0.1 +Version: 4.0.2 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build ++++++ cluster.fwd ++++++ --- /var/tmp/diff_new_pack.JqlsHe/_old 2018-01-06 18:51:01.702075667 +0100 +++ /var/tmp/diff_new_pack.JqlsHe/_new 2018-01-06 18:51:01.702075667 +0100 @@ -6,6 +6,7 @@ # 5560 for mgmtd # 7630 for hawk or hawk2 # 21064 for dlm +# 5403 for corosync qdevice(default) TCP="30865 5560 7630 21064" # space separated list of allowed UDP ports ++++++ yast2-cluster-4.0.1.tar.bz2 -> yast2-cluster-4.0.2.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/package/cluster.fwd new/yast2-cluster-4.0.2/package/cluster.fwd --- old/yast2-cluster-4.0.1/package/cluster.fwd 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/package/cluster.fwd 2018-01-05 10:40:16.000000000 +0100 @@ -6,6 +6,7 @@ # 5560 for mgmtd # 7630 for hawk or hawk2 # 21064 for dlm +# 5403 for corosync qdevice(default) TCP="30865 5560 7630 21064" # space separated list of allowed UDP ports diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/package/yast2-cluster.changes new/yast2-cluster-4.0.2/package/yast2-cluster.changes --- old/yast2-cluster-4.0.1/package/yast2-cluster.changes 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/package/yast2-cluster.changes 2018-01-05 10:40:16.000000000 +0100 @@ -1,4 +1,10 @@ ------------------------------------------------------------------- +Fri Dec 22 07:04:15 UTC 2017 - nw...@suse.com + +- bsc#1070961, support corosync qdevice. +- Version 4.0.2 + +------------------------------------------------------------------- Fri Dec 1 06:08:41 UTC 2017 - nw...@suse.com - fate#323526, support python3. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/package/yast2-cluster.spec new/yast2-cluster-4.0.2/package/yast2-cluster.spec --- old/yast2-cluster-4.0.1/package/yast2-cluster.spec 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/package/yast2-cluster.spec 2018-01-05 10:40:16.000000000 +0100 @@ -18,7 +18,7 @@ Name: yast2-cluster %define _fwdefdir /etc/sysconfig/SuSEfirewall2.d/services -Version: 4.0.1 +Version: 4.0.2 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/src/include/cluster/common.rb new/yast2-cluster-4.0.2/src/include/cluster/common.rb --- old/yast2-cluster-4.0.1/src/include/cluster/common.rb 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/src/include/cluster/common.rb 2018-01-05 10:40:16.000000000 +0100 @@ -39,16 +39,17 @@ Yast.import "SuSEFirewall" Yast.import "SuSEFirewallServices" - @DIALOG = ["communication", "security", "csync2", "conntrack", "service"] + @DIALOG = ["communication", "corosyncqdevice", "security", "csync2", "conntrack", "service"] @PARENT = {} @NAME = { - "communication" => _("Communication Channels"), - "security" => _("Security"), - "service" => _("Service"), - "csync2" => _("Configure Csync2"), - "conntrack" => _("Configure conntrackd") + "communication" => _("Communication Channels"), + "corosyncqdevice" => _("Corosync Qdevice"), + "security" => _("Security"), + "service" => _("Service"), + "csync2" => _("Configure Csync2"), + "conntrack" => _("Configure conntrackd") } end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/src/include/cluster/dialogs.rb new/yast2-cluster-4.0.2/src/include/cluster/dialogs.rb --- old/yast2-cluster-4.0.1/src/include/cluster/dialogs.rb 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/src/include/cluster/dialogs.rb 2018-01-05 10:40:16.000000000 +0100 @@ -161,7 +161,7 @@ idset = Set[] Builtins.foreach(Cluster.memberaddr) do |value| - if value[:nodeid].to_i <= 0 + if value[:nodeid].to_i <= 0 Popup.Message(_("Node ID has to be fulfilled with a positive integer")) UI.ChangeWidget(:memberaddr, :CurrentItem, i) i = 0 @@ -529,7 +529,7 @@ UI.ChangeWidget(Id(:autoid), :Value, Cluster.autoid) UI.ChangeWidget(Id(:cluster_name), :Value, Cluster.cluster_name) UI.ChangeWidget(Id(:expected_votes), :Value, Cluster.expected_votes) - UI.ChangeWidget(:expected_votes, :ValidChars, "0123456789" ); + UI.ChangeWidget(:expected_votes, :ValidChars, "0123456789"); UI.ChangeWidget(Id(:transport), :Value, Cluster.transport) @@ -708,6 +708,205 @@ nil end + def ValidateCorosyncQdevice + if UI.QueryWidget(Id(:corosync_qdevice), :Value) == false + return true + end + + if UI.QueryWidget(Id(:qdevice_model), :Value) != "net" + Popup.Message(_("The model basically defines the type of arbitrator, currently only net is support")) + UI.SetFocus(:qdevice_model) + return false + end + + if UI.QueryWidget(Id(:qdevice_votes), :Value).to_i <= 0 + Popup.Message(_("Qdevice votes must be a positive integer")) + UI.SetFocus(:qdevice_votes) + return false + end + + if !IP.Check(UI.QueryWidget(Id(:qdevice_host), :Value)) + Popup.Message(_("Qdevice host mush have a valid IP address")) + UI.SetFocus(:qdevice_host) + return false + end + + if UI.QueryWidget(Id(:qdevice_port), :Value).to_i <= 0 + Popup.Message(_("The corosync qdevice port must be a positive integer")) + UI.SetFocus(Id(:qdevice_port)) + return false + end + + if !["ffsplit", "lms", "test", "2nodelms"].include?(UI.QueryWidget(Id(:qdevice_algorithm), :Value)) + Popup.Message(_("The algorithm only can be one of the ffsplit, lms, test or 2nodelms." \ + "YaST will overwrite test and 2nodelms.")) + UI.SetFocus(Id(:algorithm)) + return false + end + + if !["lowest", "highest"].include?(UI.QueryWidget(Id(:qdevice_tie_breaker), :Value)) && + (UI.QueryWidget(Id(:qdevice_tie_breaker), :Value).to_i <= 0) + Popup.Message(_("The tie breaker can be one of lowest, highest or a valid node id (number)")) + UI.SetFocus(Id(:qdevice_tie_breaker)) + return false + end + + true + end + + def SaveCorosyncQdevice + Cluster.corosync_qdevice = Convert.to_boolean(UI.QueryWidget(Id(:corosync_qdevice), :Value)) + + Cluster.qdevice_model = UI.QueryWidget(Id(:qdevice_model), :Value) + Cluster.qdevice_votes = UI.QueryWidget(Id(:qdevice_votes), :Value).to_s + + Cluster.qdevice_host = UI.QueryWidget(Id(:qdevice_host), :Value) + Cluster.qdevice_port = UI.QueryWidget(Id(:qdevice_port), :Value).to_s + Cluster.qdevice_tls = UI.QueryWidget(Id(:qdevice_tls), :Value) + Cluster.qdevice_algorithm = UI.QueryWidget(Id(:qdevice_algorithm), :Value) + Cluster.qdevice_tie_breaker = UI.QueryWidget(Id(:qdevice_tie_breaker), :Value) + nil + end + + def CorosyncQdeviceLayout + qdevice_section = VBox( + HBox( + ComboBox( + Id(:qdevice_model), + Opt(:hstretch), + _("Qdevice model:"), + ["net"] + ), + Left(InputField(Id(:qdevice_votes),Opt(:hstretch), _("Qdevice votes:"),"")) + ) + ) + + qdevice_net_section = VBox( + HBox( + Left(InputField(Id(:qdevice_host),Opt(:hstretch), _("Qnetd server host:"),"")), + HSpacing(1), + Left(InputField(Id(:qdevice_port),Opt(:hstretch), _("Qnetd server TCP port:"),"5403")) + ), + HBox( + Left(ComboBox( + Id(:qdevice_tls), Opt(:hstretch), _("TLS:"), + ["off", "on", "required"] + )), + Left(ComboBox( + Id(:qdevice_algorithm), Opt(:hstretch, :notify), _("Algorithm:"), + [ + Item(Id("ffsplit"), "ffsplit"), + Item(Id("lms"), "lms") + ] + )), + HSpacing(1), + Left(InputField(Id(:qdevice_tie_breaker),Opt(:hstretch), _("Tie breaker:"),"lowest")) + ) + ) + + contents = VBox( + VSpacing(1), + CheckBoxFrame( + Id(:corosync_qdevice), + Opt(:hstretch, :notify), + _("En&able Corosync Qdevice"), + false, + VBox( + VSpacing(1), + qdevice_section, + VSpacing(2), + qdevice_net_section, + ) + ), + VStretch() + ) + + my_SetContents("corosyncqdevice", contents) + + UI.ChangeWidget(Id(:corosync_qdevice), :Value, Cluster.corosync_qdevice) + + UI.ChangeWidget(Id(:qdevice_model), :Value, Cluster.qdevice_model) + UI.ChangeWidget(Id(:qdevice_votes), :Value, Cluster.qdevice_votes) + UI.ChangeWidget(Id(:qdevice_votes), :ValidChars, "0123456789"); + + UI.ChangeWidget(Id(:qdevice_host), :Value, Cluster.qdevice_host) + UI.ChangeWidget(Id(:qdevice_port), :Value, Cluster.qdevice_port) + UI.ChangeWidget(Id(:qdevice_tls), :Value, Cluster.qdevice_tls) + UI.ChangeWidget(Id(:qdevice_algorithm), :Value, Cluster.qdevice_algorithm) + UI.ChangeWidget(Id(:qdevice_tie_breaker), :Value, Cluster.qdevice_tie_breaker) + + nil + end + + def UpdateQdeviceVotes + is_ffsplit = UI.QueryWidget(Id(:qdevice_algorithm), :Value) == "ffsplit" + + if is_ffsplit + UI.ChangeWidget(Id(:qdevice_votes), :Value, "1") + end + + UI.ChangeWidget(Id(:qdevice_votes), :Enabled, !is_ffsplit) + + nil + end + + def CorosyncQdeviceDialog + ret = nil + + CorosyncQdeviceLayout() + + while true + UpdateQdeviceVotes() + + ret = UI.UserInput + + if ret == :corosync_qdevice + if UI.QueryWidget(Id(:corosync_qdevice), :Value) == false + next + end + end + + if ret == :next || ret == :back + val = ValidateCorosyncQdevice() + if val == true + SaveCorosyncQdevice() + break + else + ret = nil + next + end + end + + if ret == :abort || ret == :cancel + if ReallyAbort() + return deep_copy(ret) + else + next + end + end + + if ret == :wizardTree + ret = Convert.to_string(UI.QueryWidget(Id(:wizardTree), :CurrentItem)) + end + + if Builtins.contains(@DIALOG, Convert.to_string(ret)) + ret = Builtins.symbolof(Builtins.toterm(ret)) + val = ValidateCorosyncQdevice() + if val == true + SaveCorosyncQdevice() + break + else + ret = nil + Wizard.SelectTreeItem("corosyncqdevice") + next + end + end + + Builtins.y2error("unexpected retcode: %1", ret) + end + deep_copy(ret) + end + def SecurityDialog ret = nil @@ -809,7 +1008,6 @@ true end - def UpdateServiceStatus ret = 0 ret = Service.Status("pacemaker") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/src/include/cluster/helps.rb new/yast2-cluster-4.0.2/src/include/cluster/helps.rb --- old/yast2-cluster-4.0.1/src/include/cluster/helps.rb 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/src/include/cluster/helps.rb 2018-01-05 10:40:16.000000000 +0100 @@ -42,6 +42,15 @@ "<p><b><big>Expected votes</big></b><br>Expect vote number for voting quorum. Will be automatically calculated when the nodelist {} section is present in corosync.conf (the list will be generated when using unicast transport) or can be specified in the quorum {} section (Expect votes should use the total node numble of the cluster). If Expected votes presents in unicast transport, the value will override the one automatically calculated.<br></p>\n" + "<p><b><big>Auto Generate Node ID</big></b><br>Nodeid is required when using IPv6. Auto node ID enabled will generate nodeid automatically.<br></p>\n" ), + "corosyncqdevice" => _( + "<p><b><big>Model</big></b><br>Specifies the model to be used. This parameter is required. corosync-qdevice is modular and is able to support multiple different models. The model basically defines what type of arbitrator is used. Currently only 'net' is supported.</p>\n" + + "<p><b><big>Votes</big></b><br>The number of votes provided to the cluster by qdevice. It should be configured as 1 when using 'ffsplit' algorithm and sum(votes_per_node) - 1 when 'lms' algorithm. Default is 1 or (number_of_nodes - 1) or generally sum(votes_per_node) - 1 based on algorithm.</p>\n" + + "<p><b><big>Host</big></b><br>Specifies the IP address or host name of the qnetd server to be used. This parameter is required.</p>\n" + + "<p><b><big>Port</big></b><br>Specifies TCP port of qnetd server. Default is 5403.</p>\n" + + "<p><b><big>TLS</big></b><br>Can be one of 'on', 'off' or 'required' and specifies if tls should be used. 'on' means a connection with TLS is attempted first, but if the server doesn't advertise TLS support then non-TLS will be used. 'off' is used then TLS is not required and it's then not even tried. This mode is the only one which doesn't need a properly initialized NSS database. 'required' means TLS is required and if the server doesn't support TLS, qdevice will exit with error message. 'on' need manually change, refer to corosync-qdevice's man page for more details. Default is 'off' in yast.</p>\n" + + "<p><b><big>Algorithm</big></b><br>Decision algorithm. Can be one of the 'ffsplit' or 'lms'. (Actually there are also 'test' and '2nodelms', both of which are mainly for developers and shouldn't be used for production clusters, so yast will convert to 'ffsplit' automatically). For a description of what each algorithm means and how the algorithms differ see their individual sections. Default value is ffsplit.</p>\n" + + "<p><b><big>Tie breaker</big></b><br>Can be one of 'lowest', 'highest' or 'valid_node_id' (number) values. It's used as a fallback if qdevice has to decide between two or more equal partitions. 'lowest' means the partition with the lowest node id is chosen. 'highest' means the partition with highest node id is chosen. And 'valid_node_id' means that the partition containing the node with the given node id is chosen. Default is 'lowest'.</p>\n" + ), "security" => _( "\n" + "<p><b><big>Enable Security Auth</big></b><br>This specifies that HMAC/SHA1 authentication should be used to authenticate all messages. It further specifies that all data should be encrypted with the sober128 encryption algorithm to protect data from eavesdropping. Enabling this option adds a 36 byte header to every message sent by totem which reduces total throughput. Encryption and authentication consume 75% of CPU cycles in aisexec as measured with gprof when enabled. For 100Mbit networks with 1500 MTU frame transmissions: A throughput of 9Mb/s is possible with 100% cpu utilization when this option is enabled on 3GHz cpus. A throughput of 10Mb/s is possible wth 20% cpu utilization when this option is disabled on 3GHz cpus. For gig-e networks with large frame transmissions: A throughput of 20Mb/s is possible when this option is enabled on 3GHz cpus. A throughput of 60Mb/s is possible when this option is disabled on 3GHz cpus. The default is on. <br></p>\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/src/include/cluster/wizards.rb new/yast2-cluster-4.0.2/src/include/cluster/wizards.rb --- old/yast2-cluster-4.0.1/src/include/cluster/wizards.rb 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/src/include/cluster/wizards.rb 2018-01-05 10:40:16.000000000 +0100 @@ -40,11 +40,12 @@ Yast.include include_target, "cluster/common.rb" @Aliases = { - "communication" => lambda { CommunicationDialog() }, - "security" => lambda { SecurityDialog() }, - "csync2" => lambda { Csync2Dialog() }, - "conntrack" => lambda { ConntrackDialog() }, - "service" => lambda { ServiceDialog() } + "communication" => lambda { CommunicationDialog() }, + "corosyncqdevice" => lambda { CorosyncQdeviceDialog() }, + "security" => lambda { SecurityDialog() }, + "csync2" => lambda { Csync2Dialog() }, + "conntrack" => lambda { ConntrackDialog() }, + "service" => lambda { ServiceDialog() } } end @@ -101,13 +102,18 @@ sequence = { "ws_start" => "communication", "communication" => { + :next => "corosyncqdevice", + :back => "communication", + :abort => :abort + }, + "corosyncqdevice" => { :next => "security", :back => "communication", :abort => :abort }, "security" => { :next => "csync2", - :back => "communication", + :back => "corosyncqdevice", :abort => :abort }, "csync2" => { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/src/modules/Cluster.rb new/yast2-cluster-4.0.2/src/modules/Cluster.rb --- old/yast2-cluster-4.0.1/src/modules/Cluster.rb 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/src/modules/Cluster.rb 2018-01-05 10:40:16.000000000 +0100 @@ -98,6 +98,17 @@ # {:addr1=>"10.16.35.105",:nodeid=>"5" }] @memberaddr = [] + @corosync_qdevice = false + + @qdevice_model = "net" + @qdevice_votes = "" + + @qdevice_host = "" + @qdevice_port = "5403" + @qdevice_tls = "off" + @qdevice_algorithm = "ffsplit" + @qdevice_tie_breaker = "lowest" + @csync2_host = [] @csync2_include = [] end @@ -153,6 +164,25 @@ nil end + def LoadCorosyncQdeviceConfig + if SCR.Read(path(".openais.quorum.device")) + @corosync_qdevice = true + end + + if @corosync_qdevice + @qdevice_model = SCR.Read(path(".openais.quorum.device.model")) + @qdevice_votes = SCR.Read(path(".openais.quorum.device.votes")).to_s + + @qdevice_host = SCR.Read(path(".openais.quorum.device.net.host")) + @qdevice_port = SCR.Read(path(".openais.quorum.device.net.port")).to_s + @qdevice_tls = SCR.Read(path(".openais.quorum.device.net.tls")) + @qdevice_algorithm = SCR.Read(path(".openais.quorum.device.net.algorithm")) + @qdevice_tie_breaker = SCR.Read(path(".openais.quorum.device.net.tie_breaker")) + end + + nil + end + def LoadClusterConfig if Convert.to_string(SCR.Read(path(".openais.totem.secauth"))) == "on" @@ -245,6 +275,8 @@ @rrpmode = "passive" if @rrpmode != "passive" && @rrpmode != "active" end + LoadCorosyncQdeviceConfig() + nil end @@ -266,6 +298,19 @@ return address_string end + def SaveCorosyncQdeviceConfig + SCR.Write(path(".openais.quorum.device.model"), @qdevice_model) + SCR.Write(path(".openais.quorum.device.votes"), @qdevice_votes) + + SCR.Write(path(".openais.quorum.device.net.host"), @qdevice_host) + SCR.Write(path(".openais.quorum.device.net.port"), @qdevice_port) + SCR.Write(path(".openais.quorum.device.net.tls"), @qdevice_tls) + SCR.Write(path(".openais.quorum.device.net.algorithm"), @qdevice_algorithm) + SCR.Write(path(".openais.quorum.device.net.tie_breaker"), @qdevice_tie_breaker) + + nil + end + def SaveClusterConfig if @secauth == true @@ -277,7 +322,9 @@ SCR.Write(path(".openais.totem.transport"), @transport) SCR.Write(path(".openais.totem.cluster_name"), @cluster_name) SCR.Write(path(".openais.totem.ip_version"), @ip_version) - SCR.Write(path(".openais.quorum.expected_votes"), @expected_votes) + if @expected_votes != "" + SCR.Write(path(".openais.quorum.expected_votes"), @expected_votes) + end # BNC#871970, only write member address when interface0 if @transport == "udpu" @@ -339,6 +386,13 @@ SCR.Write(path(".openais.totem.autoid"), "no") end SCR.Write(path(".openais.totem.rrpmode"), @rrpmode) + + if @corosync_qdevice + SaveCorosyncQdeviceConfig() + else + SCR.Write(path(".openais.quorum.device"), "") + end + SCR.Write(path(".openais"), "") nil @@ -543,13 +597,21 @@ udp_ports = Builtins.add(udp_ports, @mcastport2) end - temp_tcp_ports = ["21064", "7630"] - tcp_ports = SuSEFirewallServices.GetNeededTCPPorts("service:cluster") - tcp_ports = Convert.convert( - Builtins.union(tcp_ports, temp_tcp_ports), - :from => "list", - :to => "list <string>" - ) + # 30865 for csync2 + # 5560 for mgmtd + # 7630 for hawk or hawk2 + # 21064 for dlm + # 5403 for corosync qdevice(default) + tcp_ports = ["30865", "5560", "21064", "7630"] + if @corosync_qdevice + tcp_ports.push(@qdevice_port) + end + #tcp_ports = SuSEFirewallServices.GetNeededTCPPorts("service:cluster") + #tcp_ports = Convert.convert( + # Builtins.union(tcp_ports, temp_tcp_ports), + # :from => "list", + # :to => "list <string>" + #) SuSEFirewallServices.SetNeededPortsAndProtocols( "service:cluster", @@ -740,6 +802,7 @@ publish :function => :SetProposalValid, :type => "void (boolean)" publish :function => :WriteOnly, :type => "boolean ()" publish :function => :LoadClusterConfig, :type => "boolean ()" + publish :function => :LoadCorosyncQdeviceConfig, :type => "boolean ()" publish :function => :SetWriteOnly, :type => "void (boolean)" publish :function => :SetAbortFunction, :type => "void (boolean ())" publish :variable => :secauth, :type => "boolean" @@ -748,6 +811,14 @@ publish :variable => :cluster_name, :type => "string" publish :variable => :ip_version, :type => "string" publish :variable => :expected_votes, :type => "string" + publish :variable => :corosync_qdevice, :type => "boolean" + publish :variable => :qdevice_model, :type => "string" + publish :variable => :qdevice_votes, :type => "string" + publish :variable => :qdevice_host, :type => "string" + publish :variable => :qdevice_port, :type => "string" + publish :variable => :qdevice_tls, :type => "string" + publish :variable => :qdevice_algorithm, :type => "string" + publish :variable => :qdevice_tie_breaker, :type => "string" publish :variable => :two_node, :type => "string" publish :variable => :config_format, :type => "string" publish :variable => :mcastport1, :type => "string" @@ -765,6 +836,7 @@ publish :variable => :transport, :type => "string" publish :variable => :memberaddr, :type => "list <string>" publish :function => :SaveClusterConfig, :type => "void ()" + publish :function => :SaveCorosyncQdeviceConfig, :type => "void ()" publish :variable => :csync2_host, :type => "list <string>" publish :variable => :csync2_include, :type => "list <string>" publish :function => :load_csync2_conf, :type => "void ()" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-cluster-4.0.1/src/servers_non_y2/ag_openais new/yast2-cluster-4.0.2/src/servers_non_y2/ag_openais --- old/yast2-cluster-4.0.1/src/servers_non_y2/ag_openais 2017-12-04 10:34:09.000000000 +0100 +++ new/yast2-cluster-4.0.2/src/servers_non_y2/ag_openais 2018-01-05 10:40:16.000000000 +0100 @@ -5,7 +5,7 @@ # Authors: Xinwei Hu <x...@suse.de> # Lukas Ocilka <loci...@suse.cz> # -# File: ag_multipath +# File: ag_openais # # License: # @@ -36,6 +36,7 @@ import copy import sys +debug = False #the option table is used to parse and write suggested_value if no options are read @@ -165,14 +166,33 @@ "allow_downscale":{"doc":"Enables allow downscale (AD) feature","type":"int", "default_value":0}, } +quorum_qdevice_option_table = { + "model":{"doc":"Specifies the model to be used, currently only (net) is supported.","type":"string","default_value":"net","suggested_value":"net"}, + "timeout":{"doc":"Specifies how often corosync-qdevice should call the votequorum_poll function.","type":"int","default_value":10000,"suggested_value":10000}, + "sync_timeout":{"doc":"Specifies how often corosync-qdevice should call the votequorum_poll function during a sync phase.","type":"int","default_value":30000,"suggested_value":30000}, + "votes":{"doc":"The number of votes provided to the cluster by qdevice. Default is sum(votes_per_node) - 1.","type":"int","default_value":0} + } + +quorum_qdevice_net_option_table = { + "host":{"doc":"Specifies the IP address or host name of the qnetd server to be used, is required.","type":"string","default_value":"0"}, + "port":{"doc":"Specifies TCP port of qnetd server.","default_value":5403, "type":"int","suggested_value":5403}, + "tls":{"doc":"Specifies if tls should be used, only off is supported by yast.","default_value":"off", "type":"string","suggested_value":"off"}, + "algorithm":{"doc":"Decision algorithm. Can be one of the ffsplit or lms.","default_value":"ffsplit", "type":"string","suggested_value":"ffsplit"}, + "tie_breaker":{"doc":"Used as a fallback if qdevice has to decide between two or more equal partitions.","default_value":"lowest", "type":"string","suggested_value":"lowest"}, + "connect_timeout":{"doc":"Timeout when corosync-qdevice is trying to connect to corosync-qnetd host. Default is (0.8 * quorum.sync_timeout).","type":"int","default_value":0}, + "force_ip_version":{"doc":"Can be one of 0|4|6 and forces the software to use the given IP version.", "default_value":0, "type":"int","suggested_value":0} + } + qb_option_table = { "ipc_type":{"doc":"This specifies type of IPC to use", "type":"string", "default_value":"native"} } -totem_options = {"interface":[]} -logging_options = {"logger_subsys":[]} +# Using [] instead of {} is because some sections like nodelist have many nodes +totem_options = {} +logging_options = {} +quorum_qdevice_options={} quorum_options={} -nodelist_options={"node":[]} +nodelist_options={} qb_options={} def strip_comments_and_pending_space(line): @@ -183,7 +203,7 @@ return strip_comments_and_pending_space(l) -def fulfill_default_logging_options (): +def fulfill_suggested_logging_options (): for opt in logging_option_table.keys(): if opt == "logger": continue sv = logging_option_table[opt].get("suggested_value", None) @@ -208,11 +228,46 @@ if v == None and sv != None: quorum_options[opt] = sv +def fulfill_suggested_quorum_qdevice_options(): + ''' + This function only be used when having quorum qdevice + ''' + # must have 'model' in 'device' + for opt in ['model']: + sv = quorum_qdevice_option_table[opt].get("suggested_value", None) + v = quorum_qdevice_options.get(opt, None) + if v == None and sv != None: + quorum_qdevice_options[opt] = sv + + # must have 'host', 'tls', 'algorithm', 'tie_breaker' in 'net' + for opt in ['host', 'tls', 'algorithm', 'tie_breaker']: + sv = quorum_qdevice_net_option_table[opt].get("suggested_value", None) + v = quorum_qdevice_options["net"].get(opt, None) + if v == None and sv != None: + quorum_qdevice_options["net"][opt] = sv + +def print_quorum_qdevice_net_options(f, indent): + ''' + indent means the level of sub-directive + ''' + f.write("\t"*indent+"net {\n") + for l in quorum_qdevice_options["net"].keys(): + f.write("\t"*(indent+1)+"#%s\n" % (quorum_qdevice_net_option_table[l]["doc"])) + f.write("\t"*(indent+1)+"%s:\t%s\n\n" % (l, quorum_qdevice_options["net"][l])) + f.write("\t"*indent+"}\n") + def print_quorum_options(f): - f.write("quorum {\n"); + f.write("quorum {\n") for key in quorum_options.keys(): - if quorum_options[key] == "": - continue + if key == "device" and is_quorum_qdevice_configured(): + f.write("\tdevice {\n") + for l in quorum_options["device"].keys(): + if l == "net": + print_quorum_qdevice_net_options(f, 2) + else: + f.write("\t\t#%s\n" % (quorum_qdevice_option_table[l]["doc"])) + f.write("\t\t%s:\t%s\n\n" % (l, quorum_qdevice_options[l])) + f.write("\t}\n") else: f.write("\t#%s\n" % (quorum_option_table[key]["doc"])) f.write("\t%s:\t%s\n\n" % (key, quorum_options[key])) @@ -221,7 +276,7 @@ def print_qb_options(f): if(len(qb_options) == 0): return - f.write("qb {\n"); + f.write("qb {\n") for key in qb_options.keys(): f.write("\t#%s\n" % (qb_option_table[key]["doc"])) f.write("\t%s:\t%s\n\n" % (key, qb_options[key])) @@ -231,7 +286,7 @@ nodelist = nodelist_options["node"] if len(nodelist) == 0: return - f.write("nodelist {\n"); + f.write("nodelist {\n") for node in nodelist: f.write("\tnode {\n") # Sort the keys like ring0 and ring1 @@ -245,6 +300,11 @@ f.write("\t}\n") f.write("}\n") +def is_quorum_qdevice_configured(): + if ("device" not in quorum_options) or (len(quorum_qdevice_options) == 0): + return False + else: + return True def print_logging_options(f): f.write("logging {\n") @@ -256,22 +316,20 @@ f.write("\t\t#%s\n\n" % (logger_subsys_option_table[l]["doc"])) f.write("\t\t%s:\t%s\n\n" % (l, log[l])) f.write("\t}\n") - continue - f.write("\t#%s\n" % (logging_option_table[key]["doc"])) - f.write("\t%s:\t%s\n\n" % (key, logging_options[key])) + else: + f.write("\t#%s\n" % (logging_option_table[key]["doc"])) + f.write("\t%s:\t%s\n\n" % (key, logging_options[key])) f.write("}\n") def print_totem_options(f): - f.write("totem {\n") - transport_protocol = totem_options.get("transport", "udp") for key in totem_options.keys(): if key == "interface": for inf in totem_options["interface"]: f.write("\tinterface {\n") for k in inf.keys(): if inf[k] == "" or k == "oldlist": - continue; + continue else: f.write("\t\t#%s\n" % (interface_option_table[k]["doc"])) f.write("\t\t%s:\t%s\n\n" % (k, inf[k])) @@ -298,7 +356,7 @@ global nodelist_options global qb_options global quorum_options - + for l in file: i = strip_comments_and_pending_space(l) if i == "": @@ -307,19 +365,29 @@ if i[-1] == "{": i = i[:-1].rsplit()[0] if i == "totem": - totem_options = opt_parser(file, totem_option_table) + totem_options = opt_parser(file, totem_option_table, "totem") elif i == "logging": - logging_options = opt_parser(file, logging_option_table) + logging_options = opt_parser(file, logging_option_table, "logging") elif i == "nodelist": - nodelist_options = opt_parser(file, {}) + nodelist_options = opt_parser(file, {}, "nodelist") elif i == "quorum": - quorum_options = opt_parser(file, quorum_option_table) + quorum_options = opt_parser(file, quorum_option_table, "quorum") elif i == "qb": - qb_options = opt_parser(file, qb_option_table) + qb_options = opt_parser(file, qb_option_table, "qb") else: pass + + if debug: + print("Reading from /etc/corosync/corosync.conf:") + print(totem_options) + print(logging_options) + print(nodelist_options) + print(qb_options) + print(quorum_options) -def opt_parser(file, options): +def opt_parser(file, options, parent): + global quorum_qdevice_options + result = {} i = "" while (i == ""): @@ -327,21 +395,29 @@ while (i[-1] != "}"): if (i[-1] == "{"): - if i.lstrip().split(" ")[0] == "interface": + if i.lstrip().split(" ")[0] == "interface" and parent == "totem": infs = result.get("interface", []) - infs.append(opt_parser(file, interface_option_table)) + infs.append(opt_parser(file, interface_option_table, "interface")) result["interface"] = infs - elif i.lstrip().split(" ")[0] == "logger_subsys": + elif i.lstrip().split(" ")[0] == "logger_subsys" and parent == "logging": logs = result.get("logger_subsys", []) - logs.append(opt_parser(file, logger_subsys_option_table)) + logs.append(opt_parser(file, logger_subsys_option_table, "logger_subsys")) result["logger_subsys"] = logs - elif i.lstrip().split(" ")[0] == "node": + elif i.lstrip().split(" ")[0] == "node" and parent == "nodelist": members = result.get("node", []) - members.append(opt_parser(file, node_option_table)) + members.append(opt_parser(file, node_option_table, "node")) result["node"] = members - elif i.lstrip().split(" ")[0] == "member": + elif i.lstrip().split(" ")[0] == "device" and parent == "quorum": + dev = result.get("device", {}) + quorum_qdevice_options = opt_parser(file, quorum_qdevice_option_table, "qdevice") + result["device"] = quorum_qdevice_options + elif i.lstrip().split(" ")[0] == "net" and parent == "qdevice": + net = result.get("net", {}) + result["net"] = opt_parser(file, quorum_qdevice_net_option_table, "net") + # member of interface is removed in the latest version + elif i.lstrip().split(" ")[0] == "member" and parent == "interface": oldmembers = result.get("oldlist", []) - oldmembers.append(opt_parser(file, old_memberlist_table)) + oldmembers.append(opt_parser(file, old_memberlist_table, "oldlist")) result["oldlist"] = oldmembers else: #y2warning("Unknown sub-directive %s found. Ignore it" % (i.lstrip().split(" ")[0])) @@ -416,7 +492,7 @@ def generateMemberString(): member_str = "" - for item in nodelist_options.get("node"): + for item in nodelist_options.get("node"): if "ring0_addr" in item: address1 = item.get("ring0_addr", None) member_str += address1 @@ -468,8 +544,10 @@ def doList(self, path): #remove the leading dot path_arr = path + if len(path_arr) == 0: + return '["allconfs"]' - if path_arr[0] == '': + if path_arr[0] == 'allconfs' and len(path_arr) == 1: return '["quorum", "totem", "nodelist"]' elif path_arr[0] == 'totem': if len(path_arr) == 1: @@ -499,6 +577,15 @@ elif path_arr[0] == 'nodelist': if len(path_arr) == 1: return '["node"]' + elif path_arr[0] == 'quorum': + if len(path_arr) == 1: + return '["provider", "device", "expected_votes", "two_node"]' + elif len(path_arr) == 2 and path_arr[1] == "device": + return '["model", "timeout", "sync_timeout", "net", "votes"]' + elif len(path_arr) == 3 and path_arr[2] == "net": + return '["host", "port", "tls", "algorithm", "tie_breaker", "connect_timeout", "force_ip_version"]' + else: + return '[]' else: return 'nil\n' @@ -506,12 +593,25 @@ if path[0] == "": return "nil\n" elif path[0] == "quorum" and len(path) >= 2: - if path[1] == "expected_votes": - return '"%s"' % quorum_options.get("expected_votes", "") - if path[1] == "two_node": - return '"%s"' % quorum_options.get("two_node", "") + if len(path) == 2 and path[1] in quorum_option_table.keys(): + return '"%s"' % quorum_options.get(path[1], + quorum_option_table[path[1]]["default_value"]) + elif len(path) == 2 and path[1] == "device": + if is_quorum_qdevice_configured(): + return "true" + else: + return "nil" + elif is_quorum_qdevice_configured() and len(path) == 3 and \ + path[1] == "device" and path[2] in quorum_qdevice_option_table.keys(): + return '"%s"' % quorum_options["device"].get(path[2], + quorum_qdevice_option_table[path[2]]["default_value"]) + elif is_quorum_qdevice_configured() and len(path) == 4 and \ + path[1] == "device" and path[2] == "net" and \ + path[3] in quorum_qdevice_net_option_table.keys(): + return '"%s"' % quorum_qdevice_options["net"].get(path[3], + quorum_qdevice_net_option_table[path[3]]["default_value"]) else: - return "" + return "nil" elif path[0] == "totem": if len(path) == 1: return "nil" @@ -588,9 +688,12 @@ def saveFile(self): - fulfill_default_logging_options() + # Only fulfill the must option + fulfill_suggested_logging_options() fulfill_suggested_totem_options() fulfill_suggested_quorum_options() + if is_quorum_qdevice_configured(): + fulfill_suggested_quorum_qdevice_options() f = open("/etc/corosync/corosync.conf.YaST2", "w") f.write("# /etc/corosync/corosync.conf file autogenerated by YaST2.\n") @@ -613,15 +716,32 @@ pass def doWrite(self, path, args): + global quorum_options + global quorum_qdevice_options + if path[0] == "": self.saveFile() return "true" elif path[0] == "quorum": - if path[1] == "expected_votes": - quorum_options["expected_votes"] = args + if len(path) == 2 and path[1] in quorum_option_table.keys(): + quorum_options[path[1]] = args + return "true" + elif len(path) == 2 and path[1] == "device" and args == "": + del(quorum_options["device"]) + quorum_qdevice_options = {} + return "true" + elif len(path) == 3 and path[1] == "device" and \ + path[2] in quorum_qdevice_option_table.keys(): + if "device" not in quorum_options: + quorum_options["device"] = quorum_qdevice_options + quorum_qdevice_options[path[2]] = args return "true" - elif path[1] == "two_node": - quorum_options["two_node"] = args + elif len(path) == 4 and path[1] == "device" and \ + path[2] == "net" and path[3] in quorum_qdevice_net_option_table.keys(): + if "net" not in quorum_qdevice_options: + quorum_qdevice_options["net"] = {} + quorum_options["device"] = quorum_qdevice_options + quorum_qdevice_options["net"][path[3]] = args return "true" else: return "false" @@ -750,10 +870,10 @@ self.args = "" self.path = "" - #y2debug ("waiting for a command"); + #y2debug ("waiting for a command") scr_command = sys.stdin.readline().strip() - #y2debug ("newline: %s" % scr_command); + #y2debug ("newline: %s" % scr_command) # eg. Read(.totem.interface.interface0.binnetaddr,"args") Write(.) p = re.compile('^`?(\w+)\s*(\(([^,]*)(,\s*(.*))?\s*\))?\s*$') @@ -798,7 +918,10 @@ while True: scr_agent.SCR_Command () - #y2debug("Command %s %s" % (scr_agent.command,scr_agent.path)); + #y2debug("Command %s %s: %s" % (scr_agent.command,scr_agent.path,scr_agent.args)) + if debug: + print("Command %s %s: %s" % (scr_agent.command, + scr_agent.path,scr_agent.args)) if (scr_agent.command == 'Dir' ): print(openais_agent.doList(scr_agent.path))