Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package yast2-ntp-client for openSUSE:Factory checked in at 2023-01-26 13:56:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-ntp-client (Old) and /work/SRC/openSUSE:Factory/.yast2-ntp-client.new.32243 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-ntp-client" Thu Jan 26 13:56:55 2023 rev:132 rq:1060832 version:4.5.3 Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-ntp-client/yast2-ntp-client.changes 2022-11-22 16:09:54.717947492 +0100 +++ /work/SRC/openSUSE:Factory/.yast2-ntp-client.new.32243/yast2-ntp-client.changes 2023-01-26 14:12:21.556878068 +0100 @@ -1,0 +2,9 @@ +Mon Jan 23 18:50:11 UTC 2023 - Michal Filka <mfi...@suse.com> + +- bsc#1188980 + - ntp dialog allows to manually set ntp source + - ntp source can be selected as pool or server + - ntp sources are written into /etc/chrony.d/pools.conf +- 4.5.3 + +------------------------------------------------------------------- Old: ---- yast2-ntp-client-4.5.2.tar.bz2 New: ---- yast2-ntp-client-4.5.3.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-ntp-client.spec ++++++ --- /var/tmp/diff_new_pack.7BC8Uu/_old 2023-01-26 14:12:22.084880908 +0100 +++ /var/tmp/diff_new_pack.7BC8Uu/_new 2023-01-26 14:12:22.088880929 +0100 @@ -1,7 +1,7 @@ # # spec file for package yast2-ntp-client # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: yast2-ntp-client -Version: 4.5.2 +Version: 4.5.3 Release: 0 Summary: YaST2 - NTP Client Configuration License: GPL-2.0-or-later @@ -55,7 +55,7 @@ Requires: yast2-ruby-bindings >= 1.0.0 Requires: rubygem(%rb_default_ruby_abi:cfa) >= 0.6.0 -Obsoletes: yast2-ntp-client-devel-doc +Obsoletes: yast2-ntp-client-devel-doc <= 3.1.23 Supplements: autoyast(ntp-client) ++++++ yast2-ntp-client-4.5.2.tar.bz2 -> yast2-ntp-client-4.5.3.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/package/yast2-ntp-client.changes new/yast2-ntp-client-4.5.3/package/yast2-ntp-client.changes --- old/yast2-ntp-client-4.5.2/package/yast2-ntp-client.changes 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/package/yast2-ntp-client.changes 2023-01-25 11:23:00.000000000 +0100 @@ -1,4 +1,13 @@ ------------------------------------------------------------------- +Mon Jan 23 18:50:11 UTC 2023 - Michal Filka <mfi...@suse.com> + +- bsc#1188980 + - ntp dialog allows to manually set ntp source + - ntp source can be selected as pool or server + - ntp sources are written into /etc/chrony.d/pools.conf +- 4.5.3 + +------------------------------------------------------------------- Mon Nov 21 14:40:27 UTC 2022 - Knut Anderssen <kanders...@suse.com> - Fix the netconfig executable path using /sbin/netconfig instead diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/package/yast2-ntp-client.spec new/yast2-ntp-client-4.5.3/package/yast2-ntp-client.spec --- old/yast2-ntp-client-4.5.2/package/yast2-ntp-client.spec 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/package/yast2-ntp-client.spec 2023-01-25 11:23:00.000000000 +0100 @@ -17,7 +17,7 @@ Name: yast2-ntp-client -Version: 4.5.2 +Version: 4.5.3 Release: 0 Summary: YaST2 - NTP Client Configuration License: GPL-2.0-or-later @@ -55,7 +55,7 @@ Requires: yast2-ruby-bindings >= 1.0.0 Requires: rubygem(%rb_default_ruby_abi:cfa) >= 0.6.0 -Obsoletes: yast2-ntp-client-devel-doc +Obsoletes: yast2-ntp-client-devel-doc <= 3.1.23 Supplements: autoyast(ntp-client) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/src/clients/ntp-client_proposal.rb new/yast2-ntp-client-4.5.3/src/clients/ntp-client_proposal.rb --- old/yast2-ntp-client-4.5.2/src/clients/ntp-client_proposal.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/src/clients/ntp-client_proposal.rb 2023-01-25 11:23:00.000000000 +0100 @@ -1,11 +1,18 @@ require "yast" +require "y2ntp_client/widgets/sources_table" + module Yast # This is used as the general interface between yast2-country # (time,timezone) and yast2-ntp-client. class NtpClientProposalClient < Client include Yast::Logger + @sources_table = nil + @source_add_button = nil + @source_remove_button = nil + @source_type_combo = nil + def main Yast.import "UI" textdomain "ntp-client" @@ -14,7 +21,6 @@ Yast.import "NetworkService" Yast.import "NtpClient" Yast.import "Service" - Yast.import "String" Yast.import "Stage" Yast.import "Package" Yast.import "Pkg" @@ -24,6 +30,11 @@ Yast.import "Timezone" Yast.import "Wizard" + @sources_table = Y2NtpClient::Widgets::SourcesTable.new(NtpClient.GetUsedNtpSources) + @source_add_button = Y2NtpClient::Widgets::SourcesAdd.new + @source_remove_button = Y2NtpClient::Widgets::SourcesRemove.new + @source_type_combo = Y2NtpClient::Widgets::SourcesType.new + # API: # # Usual *_proposal functions: MakeProposal, AskUser, Write. @@ -64,6 +75,14 @@ when "MakeProposal" @ret = MakeProposal() when "Write" + # compatibility layer for yast-country. Yast-country works with array of servers + # which were obtained from here via dhcp_ntp_servers call. And dhcp can never + # provide pool according to @see RFC 2132 + if @param["servers"].is_a?(Array) + @param["servers"] = @param["servers"].each_with_object({}) do |addr, acc| + acc[addr] = :server + end + end @ret = Write(@param) when "ui_help_text" @ret = ui_help_text @@ -124,7 +143,14 @@ else UI.ChangeWidget(Id(:ntp_now), :Enabled, enabled) end + UI.ChangeWidget(Id(:ntp_save), :Enabled, enabled) + UI.ChangeWidget(Id(:ntp_address), :Enabled, enabled) + + @sources_table.send(enabled ? :enable : :disable) + @source_add_button.send(enabled ? :enable : :disable) + @source_remove_button.send(enabled ? :enable : :disable) + @source_type_combo.send(enabled ? :enable : :disable) end if UI.WidgetExists(Id(:ntp_configure)) # bnc#483787 @@ -174,52 +200,102 @@ NtpClient.ProcessNtpConf end - if select_ntp_server - ntp_items = fallback_ntp_items - # Once read or proposed any config we consider it as read (bnc#427712) - NtpClient.config_has_been_read = true - - log.info "ntp_items :#{ntp_items}" - UI.ChangeWidget(Id(:ntp_address), :Items, ntp_items) - if !Stage.initial - UI.ChangeWidget(Id(:ntp_address), :Value, - NtpClient.GetUsedNtpServers.first) - end - end + # preparing known ntp sources to be offered by default. Propose a random pool from + # control file by default + require "y2network/ntp_server" + source = Y2Network::NtpServer.default_servers.map(&:hostname).sample + ntp_sources = source ? { source => :pool } : {} + + # propose only when a proposal was not made yet + if !NtpClient.config_has_been_read + # if something was already stored internally, clear it and update according to the proposal + NtpClient.ntp_conf.clear_sources + + ntp_sources.each { |addr, type| NtpClient.ntp_conf.send("add_#{type}".downcase, addr) } + @sources_table.sources = ntp_sources + end + + # Dhcp provided sources are "special" according to fate#323454 it is not pre-configured + # by default, but will be offered + ntp_sources = dhcp_ntp_items.merge(ntp_sources) + + # initialize the combo of suggested ntp sources (not selected to be stored, just hint + # for user). We use timezone based list of ntp sources in addition to dhcp ones for that + ntp_sources = ntp_sources.merge(timezone_ntp_items) + ntp_items = ntp_sources + .merge(NtpClient.GetUsedNtpSources) + .keys + .map { |a| Item(Id(a), a) } + UI.ChangeWidget(Id(:ntp_address), :Items, ntp_items) + + # get in sync some prefilled values @see sources_table and @see ntp_source_input_widget + # get in sync proposal and internal state + @source_type_combo.value = ntp_sources.values.first + + # Once read or proposed any config we consider it as read (bnc#427712) + NtpClient.config_has_been_read = true nil end + # Creates a widget representing currently configured ntp servers + # + # @return YUI widget + def ntp_sources_list_table + to_yui_term(@sources_table) + end + + # Creates an add button widget + # + # Intended for modifying sources table (@see ntp_sources_list_table) + # + # @return YUI widget + def ntp_source_add_button + to_yui_term(@source_add_button) + end + + # Creates a remove button widget + # + # Intended for modifying sources table (@see ntp_sources_list_table) + # + # @return YUI widget + def ntp_source_remove_button + to_yui_term(@source_remove_button) + end + + # Creates a combo for selecting source type + # + # Currently supported types are "Pool" or "Server" (@see ntp_sources_list_table) + # + # @return YUI widget + def ntp_source_type_combo + to_yui_term(@source_type_combo) + end + + # @param [AbstractWidget] widget a widget from new CWM model class tree + # @return [::CWM::UITerm] term for libyui + def to_yui_term(widget) + # Warning: Close your eyes + # Still looking? OK, so + # - we're going to translate CWM widgets + # - we have to bcs only reason for this (and related methods) is that it creates + # part of dialog (in fact modifies on the fly) which is constructed in yast2-country. + # We cannot use whole power of CWM and have to "emulate it" + # - involved methods are at least ui_init (creates relevant part of the dialog) and + # ui_handle (processes user's input) + CWM.prepareWidget(widget.cwm_definition)["widget"] + # You can open eyes now + end + # @param [Yast::Term] replace_point id of replace point which should be used # @param [Boolean] first_time when asking for first time, we check if service is running # @return should our radio button be selected def ui_init(replace_point, first_time) - if select_ntp_server - ntp_server_widget = ComboBox( - Id(:ntp_address), - Opt(:editable, :hstretch), - # TRANSLATORS: combo box label - _("&NTP Server Address") - ) - else - # Only show all ntp servers - text = _("Synchronization Servers:\n").dup - counter = (NtpClient.GetUsedNtpServers.size > 3) ? 3 : NtpClient.GetUsedNtpServers.size - counter.times do |i| - text << NtpClient.GetUsedNtpServers[i] - text << "\n" - end - if NtpClient.GetUsedNtpServers.size > 3 - # TRANSLATOR %{count} number of additional servers - text << format(_("... (%{count} more servers)"), - count: (NtpClient.GetUsedNtpServers.size - counter)) - end - ntp_server_widget = Label(text) - end + log.info("ui_init - enter") if Stage.initial # TRANSLATORS: push button label - ntp_server_action_widget = Left(PushButton(Id(:ntp_now), _("S&ynchronize now"))) + ntp_server_action_widget = PushButton(Id(:ntp_now), _("S&ynchronize now")) save_run_widget = VBox( HBox( HSpacing(0.5), @@ -247,31 +323,36 @@ save_run_widget = VBox() end - cont = VBox( - VSpacing(0.5), - HBox( - HSpacing(3), - HWeight( - 1, - Left( - ntp_server_widget - ) + cont = HBox( + VBox( + Left( + ntp_sources_list_table ), - HWeight( - 1, - VBox( - # In TextMode and empty label is not filling an extra space, so - # an explicit vertical space was added in order to move down the - # push button being aligned with the combo box input. - UI.TextMode ? VSpacing(1) : Label(""), - ntp_server_action_widget + Left( + VSquash( + HBox( + Bottom( + ntp_source_type_combo + ), + Bottom( + ntp_source_input_widget + ), + Bottom( + ntp_source_add_button + ) + ) ) ) ), - HBox( - HSpacing(3), - HWeight( - 1, + Top( + VBox( + Left( + ntp_server_action_widget + ), + Left( + ntp_source_remove_button + ), + VSpacing(1), save_run_widget ) ) @@ -314,19 +395,22 @@ end # Writes configuration for ntp client. - # @param ntp_servers [Array<String>] list of servers to configure as ntp sync sources + # @param ntp_sources [Hash<String, Symbol>] ntp sources ({ "address" => <:pool|:server> }) # @param ntp_server [String] fallback server that is used if `ntp_servers` param is empty. # @param run_service [Boolean] define if synchronize with systemd services or via systemd timer # @return true - def WriteNtpSettings(ntp_servers, ntp_server, run_service) - ntp_servers = deep_copy(ntp_servers) + def WriteNtpSettings(ntp_sources, ntp_server, run_service) + ntp_sources = deep_copy(ntp_sources) NtpClient.modified = true - if select_ntp_server - # The user has changed the ntp-server(s). So we are writing them. - NtpClient.ntp_conf.clear_pools - ntp_servers << ntp_server if ntp_servers.empty? - ntp_servers.each do |server| - NtpClient.ntp_conf.add_pool(server) + + # reason for this ... historical + ntp_sources = { ntp_server => :server } if ntp_sources.empty? + + if !ntp_sources.empty? + # Servers list available. So we are writing them. + NtpClient.ntp_conf.clear_sources + ntp_sources.each_pair do |addr, type| + NtpClient.ntp_conf.send("add_#{type}", addr) end end if run_service @@ -355,7 +439,7 @@ # # @param [Hash] params # @option params [String] "server" The NTP server address, taken from the UI if empty - # @option params [Array<String>] "servers" A collection of NTP servers + # @option params [Hash<String, Symbol>] ntp sources ( { "address" => <:pool | :server> }) # @option params [Boolean] "run_service" Whether service should be active and enable # @option params [Boolean] "write_only" If only is needed to write the settings, (bnc#589296) # @option params [Boolean] "ntpdate_only" ? TODO: rename to onetime @@ -370,14 +454,9 @@ params.compact! ntp_server = params.fetch("server", "") - ntp_servers = params.fetch("servers", NtpClient.GetUsedNtpServers) + ntp_servers = params.fetch("servers", NtpClient.GetUsedNtpSources) run_service = params.fetch("run_service", NtpClient.run_service) - # Get the ntp_server value from UI only if isn't present (probably wasn't given as parameter) - if ntp_server.strip.empty? && select_ntp_server - ntp_server = UI.QueryWidget(Id(:ntp_address), :Value) || "" - end - return :invalid_hostname if !ntp_server.empty? && !ValidateSingleServer(ntp_server) add_or_install_required_package unless params["write_only"] @@ -389,11 +468,7 @@ # Only if network is running try to synchronize # the ntp server. if NetworkService.isNetworkRunning && !Service.Active(NtpClient.service_name) - ntp_servers = [ntp_server] - if !select_ntp_server - # Taking also the rest of the ntp servers, configured in the ntp client module. - ntp_servers += NtpClient.GetUsedNtpServers unless NtpClient.GetUsedNtpServers.nil? - end + ntp_servers = [ntp_server] + ntp_servers.keys ntp_servers.delete("") ntp_servers.uniq exit_code = 0 @@ -414,6 +489,21 @@ def ui_handle(input) redraw = false case input + when @source_add_button.widget_id + ntp_source_address = UI.QueryWidget(Id(:ntp_address), :Value) + ntp_source_type = @source_type_combo.value + ntp_source = { ntp_source_address => ntp_source_type } + + NtpClient.ntp_conf.send("add_#{ntp_source_type}".downcase, ntp_source_address) + + @sources_table.sources = @sources_table.sources.merge(ntp_source) + when @source_remove_button.widget_id + ntp_source_address = @sources_table.value + ntp_source_type = @source_type_combo.value + + NtpClient.ntp_conf.send("delete_#{ntp_source_type}".downcase, ntp_source_address) + + @sources_table.remove_item(ntp_source_address) when :ntp_configure rv = AskUser() if rv == :invalid_hostname @@ -462,6 +552,8 @@ if Stage.initial Ops.set(argmap, "ntpdate_only", true) if UI.QueryWidget(Id(:ntp_save), :Value) == false Ops.set(argmap, "run_service", true) if UI.QueryWidget(Id(:run_service), :Value) + + argmap["servers"] = @sources_table.sources end rv = Write(argmap) @@ -470,7 +562,8 @@ # So we are done here. return true unless select_ntp_server - server = Convert.to_string(UI.QueryWidget(Id(:ntp_address), :Value)) + # we get [ "address", :<type> ], we need only address here + server = @sources_table.sources.first[0] Builtins.y2milestone("ui_try_save argmap %1", argmap) if rv == :invalid_hostname handle_invalid_hostname(server) @@ -490,8 +583,9 @@ ) return false # loop on elsif !Ops.get_boolean(argmap, "ntpdate_only", false) + # user explicitly wanted to get what (s)he configured, give it to him WriteNtpSettings( - [], + @sources_table.sources, server, Ops.get_boolean(argmap, "run_service", false) ) # may be the server is realy not accessable @@ -522,28 +616,35 @@ # Public list of ntp servers Yast::Term items with the ntp address ID and # label # - # @return [Array<Yast::Term>] ntp address Item + # @return [Hash<String, Symbol>] ntp address and its type (server / pool) def timezone_ntp_items timezone_country = Timezone.GetCountryForTimezone(Timezone.timezone) servers = NtpClient.country_ntp_servers(timezone_country) # Select the first occurrence of pool.ntp.org as the default option (bnc#940881) - selected = servers.find { |s| s.hostname.end_with?("pool.ntp.org") } - servers.map do |server| - Item(Id(server.hostname), server.hostname, server.hostname == selected) + servers.find { |s| s.hostname.end_with?("pool.ntp.org") } + servers.each_with_object({}) do |server, acc| + # currently no way how to safely decide whether the source is pool or server + # so use pool as default (either it is from pool.ntp.org or we cannot decide for sure) + acc[server.hostname] = :pool end end # List of dhcp ntp servers Yast::Term items with the ntp address ID and # label # - # @return [Array<Yast::Term>] ntp address table Item + # @return [Hash<String, Symbol>] ntp address and its type (server / pool) def dhcp_ntp_items - NtpClient.dhcp_ntp_servers.map { |s| Item(Id(s.hostname), s.hostname) } + NtpClient.dhcp_ntp_servers.each_with_object({}) do |server, acc| + # dhcp can contain only an IP addresses in option 042 + # (This option specifies a list of IP addresses indicating NTP + # servers available to the client. @see RFC 2132) + acc[server.hostname] = :server + end end # List of ntp servers Yast::Term items with the ntp address ID and label # - # @return [Array<Yast::Term>] ntp address table Item + # @return [Hash<String, Symbol>] ntp address and its type (server / pool) def fallback_ntp_items return @cached_fallback_ntp_items if @cached_fallback_ntp_items @@ -569,12 +670,25 @@ # module which is not defined in the combo box. In that case we do not offer # a selection. The user should go back to ntp-client to change it. if NtpClient.GetUsedNtpServers.size == 1 - ret = fallback_ntp_items.any? do |item| - item.params[1] == NtpClient.GetUsedNtpServers.first + ret = fallback_ntp_items.keys.any? do |item| + item == NtpClient.GetUsedNtpServers.first end end ret end + + # Widget for entering custom ntp source configuration + def ntp_source_input_widget + MinWidth( + 20, + ComboBox( + Id(:ntp_address), + Opt(:editable, :hstretch), + # TRANSLATORS: combo box label, ntp source can be either "server" or "pool" + _("&NTP Source Address") + ) + ) + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/src/include/ntp-client/dialogs.rb new/yast2-ntp-client-4.5.3/src/include/ntp-client/dialogs.rb --- old/yast2-ntp-client-4.5.2/src/include/ntp-client/dialogs.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/src/include/ntp-client/dialogs.rb 2023-01-25 11:23:00.000000000 +0100 @@ -28,12 +28,6 @@ @widgets = nil end - # Display the popup to confirm abort - # @return [Boolean] true if confirmed - def abortPopup - Popup.ReallyAbort(true) - end - # Read settings dialog # @return `abort if aborted and `next otherwise def ReadDialog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/src/lib/cfa/chrony_conf.rb new/yast2-ntp-client-4.5.3/src/lib/cfa/chrony_conf.rb --- old/yast2-ntp-client-4.5.2/src/lib/cfa/chrony_conf.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/src/lib/cfa/chrony_conf.rb 2023-01-25 11:23:00.000000000 +0100 @@ -10,7 +10,7 @@ # @see http://www.rubydoc.info/github/config-files-api/config_files_api/CFA/AugeasParser # class ChronyConf < ::CFA::BaseModel - PATH = "/etc/chrony.conf".freeze + PATH = "/etc/chrony.d/pool.conf".freeze def initialize(file_handler: nil) super(CFA::AugeasParser.new("chrony.lns"), PATH, file_handler: file_handler) @@ -36,27 +36,13 @@ # When hash is used, then format is that key is option name and # value is either nil for keyword options or String with value for key value options def add_pool(address, options = :default) - options = default_pool_options if options == :default - # if there is already pool entry, place it after, if not, try use comment - existing_pools = pure_pools - matcher = if existing_pools.empty? - # for now first chrony have pools under comment mentioning pool.ntp.org - # so try to place it below - Matcher.new { |k, v| k.start_with?("#comment") && v =~ /www\.pool\.ntp\.org/ } - else - # place after the last pool available - Matcher.new(key: existing_pools.last[:key], - value_matcher: existing_pools.last[:value]) - end - placer = AfterPlacer.new(matcher) - - key = "pool[]" - value = AugeasTreeValue.new(AugeasTree.new, address) - options.each_pair do |k, v| - value.tree[k] = v - end + add_source("pool[]", address, options) { pure_pools } + end - data.add(key, value, placer) + def add_server(address, options = :default) + # we can have multiple servers defined (=> same keys). Augeas stores it as a collection + # that's why [] is added to the key + add_source("server[]", address, options) { pure_servers } end # modifies pool entry with original address to new adress and specified options @@ -86,6 +72,12 @@ data.delete(matcher) end + def delete_server(address) + matcher = server_matcher(address) + + data.delete(matcher) + end + def default_pool_options { "iburst" => nil } end @@ -95,24 +87,24 @@ data.delete(POOLS_MATCHER) end + def clear_servers + data.delete(SERVERS_MATCHER) + end + + def clear_sources + clear_pools + clear_servers + end + # returns copy of available pools # TODO allow modify of specific pool # hash with key server and value is options hash def pools - pools_data = pure_pools.map { |p| p[:value] } - pools_map = pools_data.map do |entry| - case entry - when String - [entry, {}] - when AugeasTreeValue - options = Hash[entry.tree.data.map { |e| [e[:key], e[:value]] }] - [entry.value, options] - else - raise "invalid pool data #{entry.inspect}" - end - end + sources(:pools) + end - Hash[pools_map] + def servers + sources(:servers) end # Is there any hardware clock settings? @@ -137,17 +129,71 @@ end POOLS_MATCHER = Matcher.new(collection: "pool") + SERVERS_MATCHER = Matcher.new(collection: "server") + + def sources(source) + sources_data = send("pure_#{source}").map { |p| p[:value] } + sources_map = sources_data.map do |entry| + case entry + when String + [entry, {}] + when AugeasTreeValue + options = Hash[entry.tree.data.map { |e| [e[:key], e[:value]] }] + [entry.value, options] + else + raise "invalid source data #{entry.inspect}" + end + end + + Hash[sources_map] + end + + def add_source(type, address, options = :default) + options = default_pool_options if options == :default + # if there is already ntp source entry, place it after, if not, try use comment + existing = yield + matcher = if existing.empty? + # for now first chrony have list of sources under comment mentioning pool.ntp.org + # so try to place it below + Matcher.new { |k, v| k.start_with?("#comment") && v =~ /www\.pool\.ntp\.org/ } + else + # place after the last pool available + Matcher.new(key: existing.last[:key], + value_matcher: existing.last[:value]) + end + placer = AfterPlacer.new(matcher) + + key = type + value = AugeasTreeValue.new(AugeasTree.new, address) + options.each_pair do |k, v| + value.tree[k] = v + end + + data.add(key, value, placer) + end # list of pools in internal data structure def pure_pools data.select(POOLS_MATCHER) end + # list of ntp servers in internal data structure + def pure_servers + data.select(SERVERS_MATCHER) + end + def pool_matcher(address) Matcher.new do |k, v| k == "pool[]" && (v.respond_to?(:value) ? v.value == address : v == address) end end + + def server_matcher(address) + Matcher.new do |k, v| + k == "server[]" && + (v.respond_to?(:value) ? v.value == address : v == address) + end + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/src/lib/y2ntp_client/client/finish.rb new/yast2-ntp-client-4.5.3/src/lib/y2ntp_client/client/finish.rb --- old/yast2-ntp-client-4.5.2/src/lib/y2ntp_client/client/finish.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/src/lib/y2ntp_client/client/finish.rb 2023-01-25 11:23:00.000000000 +0100 @@ -38,17 +38,22 @@ # User config from installation time: # fortunately so far we only have the server address(es) pools = Yast::NtpClient.ntp_conf.pools - log.info "pools added during installation #{pools.inspect}" + log.info "NTP pools added during installation #{pools.inspect}" + servers = Yast::NtpClient.ntp_conf.servers + log.info "NTP servers added during installation #{servers.inspect}" # ntp.conf from the RPM Yast::NtpClient.config_has_been_read = false Yast::NtpClient.ProcessNtpConf # put users server(s) back - Yast::NtpClient.ntp_conf.clear_pools + Yast::NtpClient.ntp_conf.clear_sources - pools.each_pair do |server, options| - Yast::NtpClient.ntp_conf.add_pool(server, options) + pools.each_pair do |pool, options| + Yast::NtpClient.ntp_conf.add_pool(pool, options) + end + servers.each_pair do |server, options| + Yast::NtpClient.ntp_conf.add_server(server, options) end Yast::NtpClient.write_only = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/src/lib/y2ntp_client/widgets/sources_table.rb new/yast2-ntp-client-4.5.3/src/lib/y2ntp_client/widgets/sources_table.rb --- old/yast2-ntp-client-4.5.2/src/lib/y2ntp_client/widgets/sources_table.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/src/lib/y2ntp_client/widgets/sources_table.rb 2023-01-25 11:23:00.000000000 +0100 @@ -0,0 +1,126 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/table" +require "cwm/widget" + +module Y2NtpClient + module Widgets + # Table displaying list of defined NTP sources. It displays its type and address. + class SourcesTable < CWM::Table + attr_reader :sources + + SOURCES = { + pool: "Pool".freeze, + server: "Server".freeze + }.freeze + + # @param sources [Hash<String, Symbol>] hash of ntp sources address (ip or url) + # and type (pool or server) + def initialize(sources = {}) + textdomain "ntp-client" + + # TODO: validation of the input + @sources = sources + end + + def header + [ + _("Type"), + _("Address") + ] + end + + def items + # <id, source-type, source-address> + @sources.map { |a, t| [a, SOURCES[t], a] } + end + + def sources=(sources) + @sources = sources + + change_items(items) + items&.each_with_object({}) { |i, acc| acc[i[1]] = i[2] } + end + + # Adds one item into table's content + # + # @param item [Array] a table item in array format (<id, column1 value, column2 value, ...) + def add_item(item) + change_items(items << item) + end + + # Removes one item from table's content + # + # @param id [any] id of table's item to remove + def remove_item(id) + updated_items = items.delete_if { |i| i[0] == id } + change_items(updated_items) + end + end + + # A button for adding an item into @see SourcesTable + class SourcesAdd < CWM::PushButton + def initialize + textdomain "ntp-client" + end + + def label + _("Add") + end + + def handle + nil + end + end + + # A button for removing an item from @see SourcesTable + class SourcesRemove < CWM::PushButton + def initialize + textdomain "ntp-client" + end + + def label + _("Remove") + end + + def handle + nil + end + end + + # A ComboBox containing varius supported types of NTP Sources + class SourcesType < CWM::ComboBox + def initialize + textdomain "ntp-client" + end + + def label + _("Source Type") + end + + def items + [ + [:pool, _("Pool")], + [:server, _("Server")] + ] + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/src/modules/NtpClient.rb new/yast2-ntp-client-4.5.3/src/modules/NtpClient.rb --- old/yast2-ntp-client-4.5.2/src/modules/NtpClient.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/src/modules/NtpClient.rb 2023-01-25 11:23:00.000000000 +0100 @@ -1,4 +1,3 @@ -# File: modules/NtpClient.ycp # Package: Configuration of ntp-client # Summary: Data for configuration of ntp-client, input and output functions. # Authors: Jiri Srain <jsr...@suse.cz> @@ -34,7 +33,7 @@ # @see #http://www.pool.ntp.org/ RANDOM_POOL_NTP_SERVERS = ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org"].freeze - NTP_FILE = "/etc/chrony.conf".freeze + NTP_FILE = "/etc/chrony.d/pool.conf".freeze TIMER_FILE = "yast-timesync.timer".freeze # The file name of systemd timer for the synchronization. @@ -64,7 +63,6 @@ Yast.import "Directory" Yast.import "FileUtils" Yast.import "Lan" - Yast.import "Language" Yast.import "Message" Yast.import "Mode" Yast.import "Package" @@ -338,6 +336,8 @@ # Read all ntp-client settings # @return true on success def Read + log.info("NtpClient::Read - enter") + return true if @config_has_been_read # We do not set help text here, because it was set outside @@ -386,6 +386,12 @@ ntp_conf.pools.keys end + # @return [Hash<String, Symbol> pair of source address and type (server, pool) + def GetUsedNtpSources + ntp_conf.servers.keys.each_with_object(sources = {}) { |s, res| res[s] = :server } + ntp_conf.pools.keys.each_with_object(sources) { |s, res| res[s] = :pool } + end + # Write all ntp-client settings # @return true on success def Write @@ -885,6 +891,7 @@ def timer_content erb_template = ::File.read(Directory.find_data_file("#{TIMER_FILE}.erb")) content = ERB.new(erb_template) + # warning on unused timeout is false positive - used in the erb loaded above timeout = @sync_interval content.result(binding) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/test/data/scr_root/etc/chrony.d/pool.conf.original new/yast2-ntp-client-4.5.3/test/data/scr_root/etc/chrony.d/pool.conf.original --- old/yast2-ntp-client-4.5.2/test/data/scr_root/etc/chrony.d/pool.conf.original 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/test/data/scr_root/etc/chrony.d/pool.conf.original 2023-01-25 11:23:00.000000000 +0100 @@ -0,0 +1,3 @@ +# Use public servers from the pool.ntp.org project. +# Please consider joining the pool (http://www.pool.ntp.org/join.html). +pool 2.opensuse.pool.ntp.org iburst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/test/ntp_client_proposal_test.rb new/yast2-ntp-client-4.5.3/test/ntp_client_proposal_test.rb --- old/yast2-ntp-client-4.5.2/test/ntp_client_proposal_test.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/test/ntp_client_proposal_test.rb 2023-01-25 11:23:00.000000000 +0100 @@ -54,18 +54,18 @@ .and_return(["de.pool.ntp.org"]) allow(subject).to receive(:select_ntp_server).and_return(true) allow(Yast::Stage).to receive(:initial).and_return(true) + allow(Yast::UI).to receive(:ChangeWidget) + allow(Y2Network::NtpServer).to receive(:default_servers).and_return({}) end context "when NTP servers were found via DHCP" do let(:dhcp_ntp_servers) { ["test.example.net"] } it "proposes only the found servers" do - expect(Yast::UI).to receive(:ChangeWidget) do |*args| + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:ntp_address), any_args) do |*args| items = args.last hostnames = items.map { |i| i[1] } - expect(hostnames).to eq( - ["test.example.net"] - ) + expect(hostnames).to include "test.example.net" end subject.MakeProposal end @@ -75,7 +75,7 @@ let(:dhcp_ntp_servers) { [] } it "proposes the known public servers for the current timezone" do - expect(Yast::UI).to receive(:ChangeWidget) do |*args| + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:ntp_address), any_args) do |*args| items = args.last hostnames = items.map { |i| i[1] } expect(hostnames).to eq(["de.pool.ntp.org"]) @@ -88,7 +88,7 @@ let(:config_was_read?) { true } it "proposes the known public servers for the current timezone" do - expect(Yast::UI).to receive(:ChangeWidget) do |*args| + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:ntp_address), any_args) do |*args| items = args.last hostnames = items.map { |i| i[1] } expect(hostnames).to eq(["de.pool.ntp.org"]) @@ -101,7 +101,7 @@ let(:ntp_was_selected?) { true } it "proposes the known public servers for the current timezone" do - expect(Yast::UI).to receive(:ChangeWidget) do |*args| + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:ntp_address), any_args) do |*args| items = args.last hostnames = items.map { |i| i[1] } expect(hostnames).to eq(["de.pool.ntp.org"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/test/ntp_client_test.rb new/yast2-ntp-client-4.5.3/test/ntp_client_test.rb --- old/yast2-ntp-client-4.5.2/test/ntp_client_test.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/test/ntp_client_test.rb 2023-01-25 11:23:00.000000000 +0100 @@ -18,10 +18,10 @@ let(:data_dir) { File.join(File.dirname(__FILE__), "data") } around do |example| - ::FileUtils.cp(File.join(data_dir, "scr_root/etc/chrony.conf.original"), - File.join(data_dir, "scr_root/etc/chrony.conf")) + ::FileUtils.cp(File.join(data_dir, "scr_root/etc/chrony.d/pool.conf.original"), + File.join(data_dir, "scr_root/etc/chrony.d/pool.conf")) change_scr_root(File.join(data_dir, "scr_root"), &example) - ::FileUtils.rm(File.join(data_dir, "scr_root/etc/chrony.conf")) + ::FileUtils.rm(File.join(data_dir, "scr_root/etc/chrony.d/pool.conf")) end # mock to allow read of scr chrooted env @@ -258,7 +258,7 @@ subject.ntp_conf.add_pool("tik.cesnet.cz") expect(subject.Write).to eq true - lines = File.read(File.join(data_dir, "scr_root/etc/chrony.conf")) + lines = File.read(File.join(data_dir, "scr_root/etc/chrony.d/pool.conf")) expect(lines.lines).to include("pool tik.cesnet.cz iburst\n") end @@ -703,7 +703,7 @@ end it "returns false if config doesn't exist" do - allow(Yast::FileUtils).to receive(:Exists).with("/etc/chrony.conf").and_return(false) + allow(Yast::FileUtils).to receive(:Exists).with("/etc/chrony.d/pool.conf").and_return(false) expect(subject.ProcessNtpConf).to eql(false) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-ntp-client-4.5.2/test/y2ntp_client/client/auto_test.rb new/yast2-ntp-client-4.5.3/test/y2ntp_client/client/auto_test.rb --- old/yast2-ntp-client-4.5.2/test/y2ntp_client/client/auto_test.rb 2022-11-21 16:11:18.000000000 +0100 +++ new/yast2-ntp-client-4.5.3/test/y2ntp_client/client/auto_test.rb 2023-01-25 11:23:00.000000000 +0100 @@ -74,7 +74,7 @@ end it "disables progress" do - expect(Yast::Progress).to receive(:set).with(false).and_return(false).twice + expect(Yast::Progress).to receive(:set).with(false).and_return(false).at_least(2) subject.write end end