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

Reply via email to