From d1c4389528b9365f3befb2ecba955fe9c7cb73d8 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Mon, 25 Jun 2018 14:17:20 +0200 Subject: [PATCH 001/223] add API to connect service and socket --- library/systemd/src/modules/systemd_service.rb | 2 +- library/systemd/test/systemd_service_test.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index 0971ead3e..a02467c04 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -72,7 +72,7 @@ class SystemdServiceClass < Module include Yast::Logger UNIT_SUFFIX = ".service".freeze - SERVICE_PROPMAP = SystemdUnit::DEFAULT_PROPMAP.merge(triggered_by: "TriggeredBy") + SERVICE_PROPMAP = SystemdUnit::DEFAULT_PROPMAP.merge(trigerred_by: "TriggeredBy") # @param service_name [String] "foo" or "foo.service" # @param propmap [SystemdUnit::PropMap] diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/systemd_service_test.rb index 405cad466..599d4c1e8 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/systemd_service_test.rb @@ -144,6 +144,19 @@ module Yast end end + describe "#socket" do + it "returns nil if service does not have socket" do + service = SystemdService.find("sshd") + expect(service.socket).to eq nil + end + + it "returns a socket that can start service" do + stub_services(service: "cups") + service = SystemdService.find("cups") + expect(service.socket).to be_a Yast::SystemdSocketClass::Socket + end + end + context "Start a service on the installation system" do it "starts a service with a specialized inst-sys helper if available" do allow(File).to receive(:exist?).with("/bin/service_start").and_return(true) From 22ea4858258d33ecf7f3b86e838e9f3d676c3f0a Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 26 Jun 2018 12:12:28 +0200 Subject: [PATCH 002/223] fix typo --- library/systemd/src/modules/systemd_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index a02467c04..0971ead3e 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -72,7 +72,7 @@ class SystemdServiceClass < Module include Yast::Logger UNIT_SUFFIX = ".service".freeze - SERVICE_PROPMAP = SystemdUnit::DEFAULT_PROPMAP.merge(trigerred_by: "TriggeredBy") + SERVICE_PROPMAP = SystemdUnit::DEFAULT_PROPMAP.merge(triggered_by: "TriggeredBy") # @param service_name [String] "foo" or "foo.service" # @param propmap [SystemdUnit::PropMap] From 6db0cb0492bcc1bfac85fa6b671bee44ad7af354 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 28 Jun 2018 14:37:14 +0200 Subject: [PATCH 003/223] add initial RFC for service configuration --- .../src/lib/yast2/service_configuration.rb | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 library/systemd/src/lib/yast2/service_configuration.rb diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb new file mode 100644 index 000000000..5378eeff3 --- /dev/null +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -0,0 +1,82 @@ +module Yast2 + # Class that holds configuration for single or multiple services. It allows + # read current status from system. It allows to modify target state. And it + # allows to write that target state so system reflects it. + # Its common usage is with {Yast2::ServiceWidget} which allows to modify + # configuration and write it when user approve whole dialog. + class ServiceConfiguration + # services managed by this configuration + attr_reader :services + + # creates new configuration that holds state for given services + # @param services services to configure + # @param reload if use reload instead of restart action. If + # service does not support reload or does not run, then restart is used. + # + # @example three services that wants reload instead of restart + # config = Yast2::ServiceConfiguration.new(s1, s2, s3, reload: true) + def initialize(*services, reload: false) + end + + # reads system status of services. Can be also used to reread current status. + # @raise when read services state failed + def read + end + + # writes services new status + # @raise when set service to target state failed + def write + end + + # returns current running status, but when read failed return `:unknown`. + # @return [:active, :inactive, :inconsistent, :unknown] current status of + # services: + # + # - `:active` when all services are active + # - `:inactive` when all services are inactive + # - `:inconsistent` when part of services is active and part not. + # Can happen only when configuration handle multiple services + # - `:unknown` when read of current status failed. + def current_status + end + + # returns currently set target action. If it is not yet set it returns + # `:nothing`. + # @return [:start, :stop, :restart, :nothing] action for all services: + # + # - `:start` to start all services. If service is already active, do nothing. + # - `:stop` to stop all services. If service already is inactive, do nothing. + # - `:restart` restart all services. Can be reload if specified during + # construction of this class. If service is inactive, it is started. + # - `:nothing` do not touch anything. + def target_action + end + + # sets target action for services + # @param action[:start, :stop, :restart, :nothing] for possible values and + # its explanation please see return value of {target_action} + def target_action= (action) + end + + # returns currently set autostart configuration. If it is not yet set it + # calls read and return current system state. + # @return [:on_boot, :on_demand, :manual, :inconsistent, :unknown] autostart + # configuration: + # + # - `:on_boot` all services start during boot. + # - `:on_demand` all sockets associated with services is enabled and + # for services that does not have socket, it starts on boot. + # - `:manual` all services and all its associated sockets is disabled. + # - `:inconsistent` mixture of states + # - `:unknown` when no state is set and read call failed. + def target_autostart + end + + # sets autostart configuration. + # @param [:on_boot, :on_demand, :manual, :inconsistent] autostart + # configuratio. For explanation please see {target_autostart} when + # `:inconsistent` means keep it as it is now. + def target_autostart=(configuration) + end + end +end From fe11872453ec1041eb59dbafdde24e2760a403a2 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 28 Jun 2018 15:14:53 +0200 Subject: [PATCH 004/223] and add RFC for service widget --- .../systemd/src/lib/yast2/service_widget.rb | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 library/systemd/src/lib/yast2/service_widget.rb diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb new file mode 100644 index 000000000..2b0a4cc23 --- /dev/null +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -0,0 +1,61 @@ +module Yast2 + # Class that represents widget that allows configuration of services. + # It uses to hold configuration {Yast2::ServiceConfiguration}. + # + # @example usage of widget with workflow with read + propose + show_dialog + write + # class Workflow + # def initialize + # service = Yast::SystemdService.find!("my_service") + # @service_configuration = Yast2::ServiceConfiguration.new(service) + # end + # + # def read + # @service_configuration.read + # end + # + # def propose + # service_configuration.target_action = :restart + # service_configuration.target_autostart = :on_demand + # end + # + # def show_dialog + # service_widget = ServiceWidget.new(@service_configuration) + # content = VBox( + # ..., + # service_widget.content + # ) + # loop do + # input = UI.UserInput + # service_widget.handle_input(input) + # ... + # end + # service_widget.store + # end + # + # def write + # service_configuration.write + # end + # end + class ServiceWidget + # creates new widget instance for given service configuration + # @param services configuration holder + def initialize(service_configuratin) + end + + # gets widget term + # @return + def content + end + + # handles event to dynamically react on user configuration. + # For events that does not happen inside widget it is ignored. + # @param event_id [Object] id of UI element that cause event + def handle_input(event_id) + end + + # Stores current configuration. Should be called always even when going + # back so configuration is persistent when going again forward. + def store + end + end +end From 196bc0025bb3c6b124a76c5af6c0443a349477f1 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 28 Jun 2018 17:28:59 +0200 Subject: [PATCH 005/223] implementation WIP --- .../src/lib/yast2/service_configuration.rb | 53 +++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb index 5378eeff3..8ec0e76f0 100644 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -1,3 +1,7 @@ +require "yast" + +Yast.import "SystemdService" + module Yast2 # Class that holds configuration for single or multiple services. It allows # read current status from system. It allows to modify target state. And it @@ -5,6 +9,8 @@ module Yast2 # Its common usage is with {Yast2::ServiceWidget} which allows to modify # configuration and write it when user approve whole dialog. class ServiceConfiguration + include Yast::Logger + # services managed by this configuration attr_reader :services @@ -16,11 +22,23 @@ class ServiceConfiguration # @example three services that wants reload instead of restart # config = Yast2::ServiceConfiguration.new(s1, s2, s3, reload: true) def initialize(*services, reload: false) + if services.any? { |s| !s.is_a?(Yast::SystemdServiceClass::Service) } + raise ArgumentError, "Services can be only Systemd Service - #{services.inspect}" + end + + @services = services + @reload = reload end # reads system status of services. Can be also used to reread current status. # @raise when read services state failed def read + # safe initial state when exception happen + @status = :unknown + @autostart = :unknown + + read_status + read_autostart end # writes services new status @@ -37,7 +55,10 @@ def write # - `:inconsistent` when part of services is active and part not. # Can happen only when configuration handle multiple services # - `:unknown` when read of current status failed. - def current_status + def status + read unless @status + + @status end # returns currently set target action. If it is not yet set it returns @@ -49,13 +70,16 @@ def current_status # - `:restart` restart all services. Can be reload if specified during # construction of this class. If service is inactive, it is started. # - `:nothing` do not touch anything. - def target_action + def action + return :nothing unless @action + + @action end # sets target action for services # @param action[:start, :stop, :restart, :nothing] for possible values and # its explanation please see return value of {target_action} - def target_action= (action) + def action= (action) end # returns currently set autostart configuration. If it is not yet set it @@ -69,14 +93,33 @@ def target_action= (action) # - `:manual` all services and all its associated sockets is disabled. # - `:inconsistent` mixture of states # - `:unknown` when no state is set and read call failed. - def target_autostart + def autostart + read unless @autostart + + @autostart end # sets autostart configuration. # @param [:on_boot, :on_demand, :manual, :inconsistent] autostart # configuratio. For explanation please see {target_autostart} when # `:inconsistent` means keep it as it is now. - def target_autostart=(configuration) + def autostart=(configuration) + end + + private + + def read_status + services_active = @services.map { |s| s.active? } + + return @status = :active if services_active.all? + return @status = :inactive if services_active.none? + @status = :inconsistent + rescue Yast::SystemctlError => e + log.error "systemctl failure: #{e.inspect}" + @status = :unknown + end + + def read_autostart end end end From de8240b40479f0a9b9b25d67833a4ea2d2bfb454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 28 Jun 2018 09:47:09 +0100 Subject: [PATCH 006/223] Extend SystemdService API --- .../systemd/src/modules/systemd_service.rb | 98 +++++++++ library/systemd/test/systemd_service_test.rb | 195 ++++++++++++++++++ 2 files changed, 293 insertions(+) diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index 0971ead3e..d5bdebba8 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -212,8 +212,106 @@ def socket? !socket.nil? end + alias_method :enable_service, :enable + private :enable_service + + # Enable a service + # + # @param mode [Symbol] Start mode (:boot or :demand). + # @return [Boolean] true if the service was successfully enabled; false otherwise. + def enable + self.start_mode = :boot + end + + alias_method :disable_service, :disable + private :disable_service + + # Disable a service + # + # If the service has an associated socket, it is disabled too. + # + # @return [Boolean] true if the service was successfully disabled; false otherwise. + def disable + self.start_mode = :manual + end + + alias_method :enabled_on_boot?, :enabled? + private :enabled_on_boot? + + # Determine whether the service is enabled or not + # + # The service can be enable to be started on boot or on demand. + # + # @return [Boolean] true if the service is enabled; false otherwise. + def enabled? + start_mode != :manual + end + + # Determine whether the service has an associated socket + # + # @return [Boolean] true if an associated socket exists; false otherwise. + def socket? + !socket.nil? + end + + # Return the start mode + # + # @return [Symbol] Start mode (:boot, :demand, :manual) + def start_mode + return :boot if enabled_on_boot? + return :demand if enabled_on_demand? + :manual + end + + # Set the service start mode + # + # See {#start_modes} to find out the supported modes for a given service (usually :boot, + # :manual and, in some cases, :demand). + # + # @see #start_modes + def start_mode=(mode) + if !start_modes.include?(mode) + log.warn "Invalid start mode: '#{mode}' for service '#{name}'" + return + end + + case mode + when :boot + enable_service + socket.disable + when :demand + disable_service + socket.enable + when :manual + disable_service + socket.disable + end + end + + # Return the list of supported start modes + # + # * :boot: The service will be started when the system boots. + # * :manual: The service is disabled and it will be started manually. + # * :demand: The service will be started on demand (using a Systemd socket). + # + # @return [Array] List of supported modes. + def start_modes + return @start_modes if @start_modes + @start_modes = [:boot, :manual] + @start_modes.insert(1, :demand) if socket? + @start_modes + end + private + # Determine whether the service is enabled on demand + # + # @return [Boolean] true if it is enabled on demand; false otherwise. + def enabled_on_demand? + return false unless socket + socket.enabled? + end + def installation_system? File.exist?(START_SERVICE_INSTSYS_COMMAND) end diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/systemd_service_test.rb index 599d4c1e8..5ef739469 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/systemd_service_test.rb @@ -157,6 +157,201 @@ module Yast end end + describe "#enabled?" do + subject(:service) { SystemdService.find("cups") } + + before do + allow(service).to receive(:start_mode).and_return(start_mode) + stub_services(service: "cups") + end + + context "when the start mode is :boot" do + let(:start_mode) { :boot } + + it "returns true" do + expect(service).to be_enabled + end + end + + context "when the start mode is :demand" do + let(:start_mode) { :demand } + + it "returns true" do + expect(service).to be_enabled + end + end + + context "when the start mode is :manual" do + let(:start_mode) { :manual } + + it "returns false" do + expect(service).to_not be_enabled + end + end + end + + describe "#start_mode" do + let(:enabled_on_boot?) { true } + let(:socket) { double("socket", enabled?: true) } + + subject(:service) { SystemdService.find("cups") } + + before do + stub_services(service: "cups") + allow(service).to receive(:socket).and_return(socket) + allow(service).to receive(:enabled_on_boot?).and_return(enabled_on_boot?) + end + + context "when the service is enabled" do + it "returns :boot" do + expect(service.start_mode).to eq(:boot) + end + end + + context "when the service is disabled" do + let(:enabled_on_boot?) { false } + + context "but the associated socket is enabled" do + it "returns :demand" do + expect(service.start_mode).to eq(:demand) + end + end + + context "and the socket is disabled" do + let(:socket) { double("socket", enabled?: false) } + + it "returns :manual" do + expect(service.start_mode).to eq(:manual) + end + end + + context "and there is no socket" do + let(:socket) { nil } + + it "returns :manual" do + expect(service.start_mode).to eq(:manual) + end + end + end + end + + describe "#start_mode=" do + subject(:service) { SystemdService.find("cups") } + let(:socket) { double("socket", disable: true) } + + before do + stub_services(service: "cups") + allow(service).to receive(:socket).and_return(socket) + allow(service).to receive(:disable) + end + + context "when no argument is given" do + it "enables the service to start on boot" do + expect(socket).to_not receive(:enable) + service.start_mode = :boot + end + end + + context "when :boot mode is given" do + it "enables the service to start on boot" do + expect(service).to receive(:enable_service) + expect(socket).to_not receive(:enable) + service.start_mode = :boot + end + end + + context "when :demand mode is given" do + it "enables the socket" do + expect(service).to_not receive(:enable_service) + expect(socket).to receive(:enable) + service.start_mode = :demand + end + end + + context "when :manual mode is given" do + it "disables the service and the socket" do + expect(service).to receive(:disable_service) + expect(socket).to receive(:disable) + service.start_mode = :manual + end + end + end + + describe "#start_modes" do + subject(:service) { SystemdService.find("cups") } + + before do + stub_services(service: "cups") + allow(service).to receive(:socket).and_return(socket) + end + + context "when an associated socket exists" do + let(:socket) { double("socket", disable: true) } + + it "returns :boot, :demand and :manual" do + expect(service.start_modes).to eq([:boot, :demand, :manual]) + end + end + + context "when no associated socket exists" do + let(:socket) { nil } + + it "returns :boot and :manual" do + expect(service.start_modes).to eq([:boot, :manual]) + end + end + end + + describe "#enable" do + subject(:service) { SystemdService.find("cups") } + + before do + stub_services(service: "cups") + end + + it "sets start_mode to :boot" do + expect(service).to receive(:start_mode=).with(:boot) + service.enable + end + end + + describe "#disable" do + subject(:service) { SystemdService.find("cups") } + + before do + stub_services(service: "cups") + end + + it "sets start_mode to :manual" do + expect(service).to receive(:start_mode=).with(:manual) + service.disable + end + end + + describe "#socket?" do + subject(:service) { SystemdService.find("cups") } + + before do + allow(service).to receive(:socket).and_return(socket) + end + + context "when there is an associated socket" do + let(:socket) { double("socket") } + + it "returns true" do + expect(service.socket?).to eq(true) + end + end + + context "when there is no associated socket" do + let(:socket) { nil } + + it "returns false" do + expect(service.socket?).to eq(false) + end + end + end + context "Start a service on the installation system" do it "starts a service with a specialized inst-sys helper if available" do allow(File).to receive(:exist?).with("/bin/service_start").and_return(true) From bb9adca5b0fbb8985583ee876593f9c2d417d0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 29 Jun 2018 12:57:44 +0100 Subject: [PATCH 007/223] Updates from code review --- .../systemd/src/modules/systemd_service.rb | 19 ++++++++----------- library/systemd/test/systemd_service_test.rb | 13 ++++++------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index d5bdebba8..b9196ab0a 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -256,10 +256,16 @@ def socket? # Return the start mode # + # See {#start_modes} to find out the supported modes for a given service (usually :boot, + # :manual and, in some cases, :demand). + # + # When the service are the service (:boot) and the socket (:demand) are enabled, + # the start mode is translated to :boot. + # # @return [Symbol] Start mode (:boot, :demand, :manual) def start_mode return :boot if enabled_on_boot? - return :demand if enabled_on_demand? + return :demand if socket && socket.enabled? :manual end @@ -271,8 +277,7 @@ def start_mode # @see #start_modes def start_mode=(mode) if !start_modes.include?(mode) - log.warn "Invalid start mode: '#{mode}' for service '#{name}'" - return + raise ArgumentError, "Invalid start mode: '#{mode}' for service '#{name}'" end case mode @@ -304,14 +309,6 @@ def start_modes private - # Determine whether the service is enabled on demand - # - # @return [Boolean] true if it is enabled on demand; false otherwise. - def enabled_on_demand? - return false unless socket - socket.enabled? - end - def installation_system? File.exist?(START_SERVICE_INSTSYS_COMMAND) end diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/systemd_service_test.rb index 5ef739469..067d40045 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/systemd_service_test.rb @@ -245,13 +245,6 @@ module Yast allow(service).to receive(:disable) end - context "when no argument is given" do - it "enables the service to start on boot" do - expect(socket).to_not receive(:enable) - service.start_mode = :boot - end - end - context "when :boot mode is given" do it "enables the service to start on boot" do expect(service).to receive(:enable_service) @@ -275,6 +268,12 @@ module Yast service.start_mode = :manual end end + + context "when an invalid value is given" do + it "raises an error" do + expect { service.start_mode = :other }.to raise_error(ArgumentError) + end + end end describe "#start_modes" do From bd052354596421e76b5b7dc017cec8418b6aea22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 29 Jun 2018 13:20:47 +0100 Subject: [PATCH 008/223] Updates from code review --- library/systemd/src/modules/systemd_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index b9196ab0a..e9b2f015c 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -259,8 +259,8 @@ def socket? # See {#start_modes} to find out the supported modes for a given service (usually :boot, # :manual and, in some cases, :demand). # - # When the service are the service (:boot) and the socket (:demand) are enabled, - # the start mode is translated to :boot. + # When the service (:boot) and the socket (:demand) are enabled, the start mode is translated + # to :boot. # # @return [Symbol] Start mode (:boot, :demand, :manual) def start_mode From 123858eef1a97c9379fd2aee51ef529a22f22036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 29 Jun 2018 13:43:09 +0100 Subject: [PATCH 009/223] Improve enable/disable tests --- library/systemd/test/systemd_service_test.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/systemd_service_test.rb index 067d40045..b8a91d0a0 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/systemd_service_test.rb @@ -303,26 +303,32 @@ module Yast describe "#enable" do subject(:service) { SystemdService.find("cups") } + let(:socket) { double("socket") } before do stub_services(service: "cups") + allow(service).to receive(:socket).and_return(socket) end - it "sets start_mode to :boot" do - expect(service).to receive(:start_mode=).with(:boot) + it "enables the service to start on boot" do + expect(service).to receive(:enable_service) + expect(socket).to receive(:disable) service.enable end end describe "#disable" do subject(:service) { SystemdService.find("cups") } + let(:socket) { double("socket") } before do stub_services(service: "cups") + allow(service).to receive(:socket).and_return(socket) end - it "sets start_mode to :manual" do - expect(service).to receive(:start_mode=).with(:manual) + it "disables the service and the socket" do + expect(service).to receive(:disable_service) + expect(socket).to receive(:disable) service.disable end end From 23bd65e78abb36b0dcb63a17ae97917a25169ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 29 Jun 2018 14:17:19 +0100 Subject: [PATCH 010/223] Keep old enable/disable SystemdService API --- .../systemd/src/modules/systemd_service.rb | 6 +-- library/systemd/test/systemd_service_test.rb | 38 ++----------------- 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index e9b2f015c..21bd9fee4 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -282,13 +282,13 @@ def start_mode=(mode) case mode when :boot - enable_service + enable socket.disable when :demand - disable_service + disable socket.enable when :manual - disable_service + disable socket.disable end end diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/systemd_service_test.rb index b8a91d0a0..bb5ca8a50 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/systemd_service_test.rb @@ -247,7 +247,7 @@ module Yast context "when :boot mode is given" do it "enables the service to start on boot" do - expect(service).to receive(:enable_service) + expect(service).to receive(:enable) expect(socket).to_not receive(:enable) service.start_mode = :boot end @@ -255,7 +255,7 @@ module Yast context "when :demand mode is given" do it "enables the socket" do - expect(service).to_not receive(:enable_service) + expect(service).to_not receive(:enable) expect(socket).to receive(:enable) service.start_mode = :demand end @@ -263,7 +263,7 @@ module Yast context "when :manual mode is given" do it "disables the service and the socket" do - expect(service).to receive(:disable_service) + expect(service).to receive(:disable) expect(socket).to receive(:disable) service.start_mode = :manual end @@ -301,38 +301,6 @@ module Yast end end - describe "#enable" do - subject(:service) { SystemdService.find("cups") } - let(:socket) { double("socket") } - - before do - stub_services(service: "cups") - allow(service).to receive(:socket).and_return(socket) - end - - it "enables the service to start on boot" do - expect(service).to receive(:enable_service) - expect(socket).to receive(:disable) - service.enable - end - end - - describe "#disable" do - subject(:service) { SystemdService.find("cups") } - let(:socket) { double("socket") } - - before do - stub_services(service: "cups") - allow(service).to receive(:socket).and_return(socket) - end - - it "disables the service and the socket" do - expect(service).to receive(:disable_service) - expect(socket).to receive(:disable) - service.disable - end - end - describe "#socket?" do subject(:service) { SystemdService.find("cups") } From ad25e74cd661e4e52d63d39f5aa0b26e35cf22ae Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 29 Jun 2018 15:20:31 +0200 Subject: [PATCH 011/223] remove reload flag and add reload state and implement some methods --- .../src/lib/yast2/service_configuration.rb | 87 +++++++++++++++++-- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb index 8ec0e76f0..45afd9252 100644 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -4,7 +4,7 @@ module Yast2 # Class that holds configuration for single or multiple services. It allows - # read current status from system. It allows to modify target state. And it + # to read current status from system. It allows to modify target state. And it # allows to write that target state so system reflects it. # Its common usage is with {Yast2::ServiceWidget} which allows to modify # configuration and write it when user approve whole dialog. @@ -19,15 +19,14 @@ class ServiceConfiguration # @param reload if use reload instead of restart action. If # service does not support reload or does not run, then restart is used. # - # @example three services that wants reload instead of restart - # config = Yast2::ServiceConfiguration.new(s1, s2, s3, reload: true) - def initialize(*services, reload: false) + # @example three services + # config = Yast2::ServiceConfiguration.new(s1, s2, s3) + def initialize(*services) if services.any? { |s| !s.is_a?(Yast::SystemdServiceClass::Service) } raise ArgumentError, "Services can be only Systemd Service - #{services.inspect}" end @services = services - @reload = reload end # reads system status of services. Can be also used to reread current status. @@ -44,6 +43,8 @@ def read # writes services new status # @raise when set service to target state failed def write + write_action + write_autostart end # returns current running status, but when read failed return `:unknown`. @@ -67,8 +68,9 @@ def status # # - `:start` to start all services. If service is already active, do nothing. # - `:stop` to stop all services. If service already is inactive, do nothing. - # - `:restart` restart all services. Can be reload if specified during - # construction of this class. If service is inactive, it is started. + # - `:restart` restart all services. If service is inactive, it is started. + # - `:reload` reload all services that support it and restart that does not + # support it. If service is inactive, it is started. # - `:nothing` do not touch anything. def action return :nothing unless @action @@ -76,10 +78,16 @@ def action @action end + ACTIONS = [ :start, :stop, :reload, :restart, :nothing].freeze # sets target action for services - # @param action[:start, :stop, :restart, :nothing] for possible values and + # @param action[:start, :stop, :restart, :reload, :nothing] for possible values and # its explanation please see return value of {target_action} def action= (action) + if !ACTIONS.include?(action) + raise ArgumentError, "Invalid parameter #{action.inspect}" + end + + @action = action end # returns currently set autostart configuration. If it is not yet set it @@ -99,11 +107,28 @@ def autostart @autostart end + AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent] # sets autostart configuration. # @param [:on_boot, :on_demand, :manual, :inconsistent] autostart - # configuratio. For explanation please see {target_autostart} when + # configuratio. For explanation please see {autostart} when # `:inconsistent` means keep it as it is now. def autostart=(configuration) + if !AUTOSTART_OPTIONS.include?(configuration) + raise ArgumentError, "Invalid parameter #{configuration.inspect}" + end + + @autostart = configuration + end + + # returns true if any service support reload + def support_reload? + # TODO: implement it + true + end + + # returns true if any service has socket start + def support_on_demand? + @services.any?(&:socket?) end private @@ -120,6 +145,50 @@ def read_status end def read_autostart + sockets = @services.map(&:socket).compact + services_without_socket = @services.reject(&:socket) + if sockets.all?(&:enabled?) && services_without_socket.all?(&:enabled?) + @autostart = :on_demand + elsif @services.all?(&:enabled?) + @autostart = :on_boot + elsif sockets.none?(&:enabled?) && @services.none?(&:enabled?) + @autostart = :manual + else + @autostart = :inconsistent + end + rescue SystemctlError =>e + log.error "systemctl failure: #{e.inspect}" + @autostart = :unknown + end + + def write_autostart + case autostart + when :on_boot then @services.each { |s| s.start_mode = :boot } + when :on_demand + @services.each do |service| + if s.start_modes.include?(:demand) + s.start_mode = :demand + else + s.start_mode = :boot + end + end + when :manual then @services.each { |s| s.start_mode = :manual } + when :inconsistent then log.info "keeping current autostart" + else + raise "Unexpected action #{autostart.inspect}" + end + end + + def write_action + case action + when :start then @services.each(&:start) + when :stop then @services.each(&:stop) + when :reload then @services.each(&:reload_or_restart) + when :restart then @services.each(&:restart) + when :nothing then log.info "no action" + else + raise "Unexpected action #{action.inspect}" + end end end end From 426072a1595dad405145142e5bb8d0e1fad644aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 29 Jun 2018 14:47:08 +0100 Subject: [PATCH 012/223] Rename :boot and :demand to :on_boot and :on_demand --- .../systemd/src/modules/systemd_service.rb | 30 ++++++++--------- library/systemd/test/systemd_service_test.rb | 32 +++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index 21bd9fee4..332f68863 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -256,23 +256,23 @@ def socket? # Return the start mode # - # See {#start_modes} to find out the supported modes for a given service (usually :boot, - # :manual and, in some cases, :demand). + # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, + # :manual and, in some cases, :on_demand). # - # When the service (:boot) and the socket (:demand) are enabled, the start mode is translated - # to :boot. + # When the service (:on_boot) and the socket (:on_demand) are enabled, the start mode is translated + # to :on_boot. # - # @return [Symbol] Start mode (:boot, :demand, :manual) + # @return [Symbol] Start mode (:on_boot, :on_demand, :manual) def start_mode - return :boot if enabled_on_boot? - return :demand if socket && socket.enabled? + return :on_boot if enabled_on_boot? + return :on_demand if socket && socket.enabled? :manual end # Set the service start mode # - # See {#start_modes} to find out the supported modes for a given service (usually :boot, - # :manual and, in some cases, :demand). + # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, + # :manual and, in some cases, :on_demand). # # @see #start_modes def start_mode=(mode) @@ -281,10 +281,10 @@ def start_mode=(mode) end case mode - when :boot + when :on_boot enable socket.disable - when :demand + when :on_demand disable socket.enable when :manual @@ -295,15 +295,15 @@ def start_mode=(mode) # Return the list of supported start modes # - # * :boot: The service will be started when the system boots. + # * :on_boot: The service will be started when the system boots. # * :manual: The service is disabled and it will be started manually. - # * :demand: The service will be started on demand (using a Systemd socket). + # * :on_demand: The service will be started on demand (using a Systemd socket). # # @return [Array] List of supported modes. def start_modes return @start_modes if @start_modes - @start_modes = [:boot, :manual] - @start_modes.insert(1, :demand) if socket? + @start_modes = [:on_boot, :manual] + @start_modes.insert(1, :on_demand) if socket? @start_modes end diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/systemd_service_test.rb index bb5ca8a50..a29d0fe28 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/systemd_service_test.rb @@ -165,16 +165,16 @@ module Yast stub_services(service: "cups") end - context "when the start mode is :boot" do - let(:start_mode) { :boot } + context "when the start mode is :on_boot" do + let(:start_mode) { :on_boot } it "returns true" do expect(service).to be_enabled end end - context "when the start mode is :demand" do - let(:start_mode) { :demand } + context "when the start mode is :on_demand" do + let(:start_mode) { :on_demand } it "returns true" do expect(service).to be_enabled @@ -203,8 +203,8 @@ module Yast end context "when the service is enabled" do - it "returns :boot" do - expect(service.start_mode).to eq(:boot) + it "returns :on_boot" do + expect(service.start_mode).to eq(:on_boot) end end @@ -212,8 +212,8 @@ module Yast let(:enabled_on_boot?) { false } context "but the associated socket is enabled" do - it "returns :demand" do - expect(service.start_mode).to eq(:demand) + it "returns :on_demand" do + expect(service.start_mode).to eq(:on_demand) end end @@ -245,19 +245,19 @@ module Yast allow(service).to receive(:disable) end - context "when :boot mode is given" do + context "when :on_boot mode is given" do it "enables the service to start on boot" do expect(service).to receive(:enable) expect(socket).to_not receive(:enable) - service.start_mode = :boot + service.start_mode = :on_boot end end - context "when :demand mode is given" do + context "when :on_demand mode is given" do it "enables the socket" do expect(service).to_not receive(:enable) expect(socket).to receive(:enable) - service.start_mode = :demand + service.start_mode = :on_demand end end @@ -287,16 +287,16 @@ module Yast context "when an associated socket exists" do let(:socket) { double("socket", disable: true) } - it "returns :boot, :demand and :manual" do - expect(service.start_modes).to eq([:boot, :demand, :manual]) + it "returns :on_boot, :on_demand and :manual" do + expect(service.start_modes).to eq([:on_boot, :on_demand, :manual]) end end context "when no associated socket exists" do let(:socket) { nil } - it "returns :boot and :manual" do - expect(service.start_modes).to eq([:boot, :manual]) + it "returns :on_boot and :manual" do + expect(service.start_modes).to eq([:on_boot, :manual]) end end end From 9d087c39c91d2e74a6a5675460f9c58eb2efae90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 29 Jun 2018 16:30:14 +0100 Subject: [PATCH 013/223] Add Yast2::SystemService class --- .../system/src/lib/yast2/system_service.rb | 122 ++++++++++++ .../system/test/yast2/system_service_test.rb | 147 ++++++++++++++ .../systemd/src/modules/systemd_service.rb | 95 --------- library/systemd/test/systemd_service_test.rb | 181 ------------------ 4 files changed, 269 insertions(+), 276 deletions(-) create mode 100644 library/system/src/lib/yast2/system_service.rb create mode 100644 library/system/test/yast2/system_service_test.rb diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb new file mode 100644 index 000000000..b5a0b9332 --- /dev/null +++ b/library/system/src/lib/yast2/system_service.rb @@ -0,0 +1,122 @@ +# encoding: utf-8 + +# Copyright (c) [2018] 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. + +Yast.import "SystemdService" + +module Yast2 + class SystemService + # @return [Yast::SystemdService] + attr_reader :service + + class << self + def find(name) + new(Yast::SystemdService.find(name)) + end + end + + # @param service [SystedService] + def initialize(service) + @service = service + end + + # Returns socket associated with service or nil if there is no such socket + def socket + return @socket if @socket + + # not triggered + socket_name = properties.triggered_by + return unless socket_name + + socket_name = socket_name[/\S+\.socket/] + return unless socket_name # triggered by non-socket + + @socket = Yast::SystemdSocket.find(socket_name) + end + + # Determine whether the service has an associated socket + # + # @return [Boolean] true if an associated socket exists; false otherwise. + def socket? + !socket.nil? + end + + # Return the start mode + # + # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, + # :manual and, in some cases, :on_demand). + # + # When the service (:on_boot) and the socket (:on_demand) are enabled, the start mode is translated + # to :on_boot. + # + # @return [Symbol] Start mode (:on_boot, :on_demand, :manual) + def start_mode + return :on_boot if service.enabled? + return :on_demand if socket && socket.enabled? + :manual + end + + # Set the service start mode + # + # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, + # :manual and, in some cases, :on_demand). + # + # @see #start_modes + def start_mode=(mode) + if !start_modes.include?(mode) + raise ArgumentError, "Invalid start mode: '#{mode}' for service '#{service.name}'" + end + + case mode + when :on_boot + service.enable + socket.disable + when :on_demand + service.disable + socket.enable + when :manual + service.disable + socket.disable + end + end + + # Return the list of supported start modes + # + # * :on_boot: The service will be started when the system boots. + # * :manual: The service is disabled and it will be started manually. + # * :on_demand: The service will be started on demand (using a Systemd socket). + # + # @return [Array] List of supported modes. + def start_modes + return @start_modes if @start_modes + @start_modes = [:on_boot, :manual] + @start_modes.insert(1, :on_demand) if socket? + @start_modes + end + + def start + raise NotImplementedError + end + + def stop + raise NotImplementedError + end + end +end diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb new file mode 100644 index 000000000..8ac5d420d --- /dev/null +++ b/library/system/test/yast2/system_service_test.rb @@ -0,0 +1,147 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2018] 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_relative "../test_helper" +require "yast2/system_service" + +describe Yast2::SystemService do + subject(:system_service) { described_class.new(service) } + + let(:service) { double("service", enabled?: true, name: "cups") } + let(:socket) { double("socket", enabled?: true) } + + before do + allow(system_service).to receive(:socket).and_return(socket) + end + + describe "#start_mode" do + context "when the service is enabled" do + it "returns :on_boot" do + expect(system_service.start_mode).to eq(:on_boot) + end + end + + context "when the service is disabled" do + let(:service) { double("service", enabled?: false) } + + context "but the associated socket is enabled" do + it "returns :on_demand" do + expect(system_service.start_mode).to eq(:on_demand) + end + end + + context "and the socket is disabled" do + let(:socket) { double("socket", enabled?: false) } + + it "returns :manual" do + expect(system_service.start_mode).to eq(:manual) + end + end + + context "and there is no socket" do + let(:socket) { nil } + + it "returns :manual" do + expect(system_service.start_mode).to eq(:manual) + end + end + end + end + + describe "#start_mode=" do + let(:socket) { double("socket", disable: true) } + + context "when :on_boot mode is given" do + it "enables the service to start on boot" do + expect(service).to receive(:enable) + expect(socket).to receive(:disable) + system_service.start_mode = :on_boot + end + end + + context "when :on_demand mode is given" do + it "enables the socket" do + expect(service).to receive(:disable) + expect(socket).to receive(:enable) + system_service.start_mode = :on_demand + end + end + + context "when :manual mode is given" do + it "disables the service and the socket" do + expect(service).to receive(:disable) + expect(socket).to receive(:disable) + system_service.start_mode = :manual + end + end + + context "when an invalid value is given" do + it "raises an error" do + expect { system_service.start_mode = :other }.to raise_error(ArgumentError) + end + end + end + + describe "#start_modes" do + before do + allow(service).to receive(:socket).and_return(socket) + end + + context "when an associated socket exists" do + let(:socket) { double("socket", disable: true) } + + it "returns :on_boot, :on_demand and :manual" do + expect(system_service.start_modes).to eq([:on_boot, :on_demand, :manual]) + end + end + + context "when no associated socket exists" do + let(:socket) { nil } + + it "returns :on_boot and :manual" do + expect(system_service.start_modes).to eq([:on_boot, :manual]) + end + end + end + + describe "#socket?" do + before do + allow(system_service).to receive(:socket).and_return(socket) + end + + context "when there is an associated socket" do + let(:socket) { double("socket") } + + it "returns true" do + expect(system_service.socket?).to eq(true) + end + end + + context "when there is no associated socket" do + let(:socket) { nil } + + it "returns false" do + expect(system_service.socket?).to eq(false) + end + end + end +end diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb index 332f68863..0971ead3e 100644 --- a/library/systemd/src/modules/systemd_service.rb +++ b/library/systemd/src/modules/systemd_service.rb @@ -212,101 +212,6 @@ def socket? !socket.nil? end - alias_method :enable_service, :enable - private :enable_service - - # Enable a service - # - # @param mode [Symbol] Start mode (:boot or :demand). - # @return [Boolean] true if the service was successfully enabled; false otherwise. - def enable - self.start_mode = :boot - end - - alias_method :disable_service, :disable - private :disable_service - - # Disable a service - # - # If the service has an associated socket, it is disabled too. - # - # @return [Boolean] true if the service was successfully disabled; false otherwise. - def disable - self.start_mode = :manual - end - - alias_method :enabled_on_boot?, :enabled? - private :enabled_on_boot? - - # Determine whether the service is enabled or not - # - # The service can be enable to be started on boot or on demand. - # - # @return [Boolean] true if the service is enabled; false otherwise. - def enabled? - start_mode != :manual - end - - # Determine whether the service has an associated socket - # - # @return [Boolean] true if an associated socket exists; false otherwise. - def socket? - !socket.nil? - end - - # Return the start mode - # - # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, - # :manual and, in some cases, :on_demand). - # - # When the service (:on_boot) and the socket (:on_demand) are enabled, the start mode is translated - # to :on_boot. - # - # @return [Symbol] Start mode (:on_boot, :on_demand, :manual) - def start_mode - return :on_boot if enabled_on_boot? - return :on_demand if socket && socket.enabled? - :manual - end - - # Set the service start mode - # - # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, - # :manual and, in some cases, :on_demand). - # - # @see #start_modes - def start_mode=(mode) - if !start_modes.include?(mode) - raise ArgumentError, "Invalid start mode: '#{mode}' for service '#{name}'" - end - - case mode - when :on_boot - enable - socket.disable - when :on_demand - disable - socket.enable - when :manual - disable - socket.disable - end - end - - # Return the list of supported start modes - # - # * :on_boot: The service will be started when the system boots. - # * :manual: The service is disabled and it will be started manually. - # * :on_demand: The service will be started on demand (using a Systemd socket). - # - # @return [Array] List of supported modes. - def start_modes - return @start_modes if @start_modes - @start_modes = [:on_boot, :manual] - @start_modes.insert(1, :on_demand) if socket? - @start_modes - end - private def installation_system? diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/systemd_service_test.rb index a29d0fe28..405cad466 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/systemd_service_test.rb @@ -144,187 +144,6 @@ module Yast end end - describe "#socket" do - it "returns nil if service does not have socket" do - service = SystemdService.find("sshd") - expect(service.socket).to eq nil - end - - it "returns a socket that can start service" do - stub_services(service: "cups") - service = SystemdService.find("cups") - expect(service.socket).to be_a Yast::SystemdSocketClass::Socket - end - end - - describe "#enabled?" do - subject(:service) { SystemdService.find("cups") } - - before do - allow(service).to receive(:start_mode).and_return(start_mode) - stub_services(service: "cups") - end - - context "when the start mode is :on_boot" do - let(:start_mode) { :on_boot } - - it "returns true" do - expect(service).to be_enabled - end - end - - context "when the start mode is :on_demand" do - let(:start_mode) { :on_demand } - - it "returns true" do - expect(service).to be_enabled - end - end - - context "when the start mode is :manual" do - let(:start_mode) { :manual } - - it "returns false" do - expect(service).to_not be_enabled - end - end - end - - describe "#start_mode" do - let(:enabled_on_boot?) { true } - let(:socket) { double("socket", enabled?: true) } - - subject(:service) { SystemdService.find("cups") } - - before do - stub_services(service: "cups") - allow(service).to receive(:socket).and_return(socket) - allow(service).to receive(:enabled_on_boot?).and_return(enabled_on_boot?) - end - - context "when the service is enabled" do - it "returns :on_boot" do - expect(service.start_mode).to eq(:on_boot) - end - end - - context "when the service is disabled" do - let(:enabled_on_boot?) { false } - - context "but the associated socket is enabled" do - it "returns :on_demand" do - expect(service.start_mode).to eq(:on_demand) - end - end - - context "and the socket is disabled" do - let(:socket) { double("socket", enabled?: false) } - - it "returns :manual" do - expect(service.start_mode).to eq(:manual) - end - end - - context "and there is no socket" do - let(:socket) { nil } - - it "returns :manual" do - expect(service.start_mode).to eq(:manual) - end - end - end - end - - describe "#start_mode=" do - subject(:service) { SystemdService.find("cups") } - let(:socket) { double("socket", disable: true) } - - before do - stub_services(service: "cups") - allow(service).to receive(:socket).and_return(socket) - allow(service).to receive(:disable) - end - - context "when :on_boot mode is given" do - it "enables the service to start on boot" do - expect(service).to receive(:enable) - expect(socket).to_not receive(:enable) - service.start_mode = :on_boot - end - end - - context "when :on_demand mode is given" do - it "enables the socket" do - expect(service).to_not receive(:enable) - expect(socket).to receive(:enable) - service.start_mode = :on_demand - end - end - - context "when :manual mode is given" do - it "disables the service and the socket" do - expect(service).to receive(:disable) - expect(socket).to receive(:disable) - service.start_mode = :manual - end - end - - context "when an invalid value is given" do - it "raises an error" do - expect { service.start_mode = :other }.to raise_error(ArgumentError) - end - end - end - - describe "#start_modes" do - subject(:service) { SystemdService.find("cups") } - - before do - stub_services(service: "cups") - allow(service).to receive(:socket).and_return(socket) - end - - context "when an associated socket exists" do - let(:socket) { double("socket", disable: true) } - - it "returns :on_boot, :on_demand and :manual" do - expect(service.start_modes).to eq([:on_boot, :on_demand, :manual]) - end - end - - context "when no associated socket exists" do - let(:socket) { nil } - - it "returns :on_boot and :manual" do - expect(service.start_modes).to eq([:on_boot, :manual]) - end - end - end - - describe "#socket?" do - subject(:service) { SystemdService.find("cups") } - - before do - allow(service).to receive(:socket).and_return(socket) - end - - context "when there is an associated socket" do - let(:socket) { double("socket") } - - it "returns true" do - expect(service.socket?).to eq(true) - end - end - - context "when there is no associated socket" do - let(:socket) { nil } - - it "returns false" do - expect(service.socket?).to eq(false) - end - end - end - context "Start a service on the installation system" do it "starts a service with a specialized inst-sys helper if available" do allow(File).to receive(:exist?).with("/bin/service_start").and_return(true) From 453b18b7ce8c4f800fe384e92a1346f64895fddf Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Mon, 2 Jul 2018 11:43:41 +0200 Subject: [PATCH 014/223] document new start behavior --- library/systemd/src/lib/yast2/service_configuration.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb index 45afd9252..d60fe408b 100644 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -67,6 +67,7 @@ def status # @return [:start, :stop, :restart, :nothing] action for all services: # # - `:start` to start all services. If service is already active, do nothing. + # if services has socket it starts socket instead of service. # - `:stop` to stop all services. If service already is inactive, do nothing. # - `:restart` restart all services. If service is inactive, it is started. # - `:reload` reload all services that support it and restart that does not @@ -181,7 +182,11 @@ def write_autostart def write_action case action - when :start then @services.each(&:start) + when :start + sockets = @services.map(&:socket).compact + services_without_socket = @services.reject(&:socket) + sockets.each(&:start) + services_without_socket.each(&:start) when :stop then @services.each(&:stop) when :reload then @services.each(&:reload_or_restart) when :restart then @services.each(&:restart) From 4914b93a3348c95d272a670a96d0374f928cc1a0 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Mon, 2 Jul 2018 17:25:00 +0200 Subject: [PATCH 015/223] add working examples --- .../systemd/examples/comparison_of_widgets.rb | 56 +++++++++ library/systemd/examples/service_widget.rb | 46 ++++++++ .../systemd/src/lib/yast2/service_widget.rb | 108 +++++++++++++++++- 3 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 library/systemd/examples/comparison_of_widgets.rb create mode 100644 library/systemd/examples/service_widget.rb diff --git a/library/systemd/examples/comparison_of_widgets.rb b/library/systemd/examples/comparison_of_widgets.rb new file mode 100644 index 000000000..70f833990 --- /dev/null +++ b/library/systemd/examples/comparison_of_widgets.rb @@ -0,0 +1,56 @@ +require "yast" + +require "yast2/service_widget" +require "yast2/service_configuration" +require "ui/service_status" + +Yast.import "SystemdService" + +def service + @service ||= Yast::SystemdService.find!("cups.service") +end + +def service_configuration + @service_configuration ||= Yast2::ServiceConfiguration.new(service) +end + +def service_widget + @service_widget ||= Yast2::ServiceWidget.new(service_configuration) +end + +def read + service_configuration.read +end + +def service_status + @ss = UI::ServiceStatus.new(service) +end + +include Yast::UIShortcuts + +def ui_loop + Yast::UI.OpenDialog( + Yast::HBox( + service_widget.content, + service_status.widget + ) + ) + loop do + input = Yast::UI.UserInput + service_widget.handle_input(input) + break if input == :cancel + end + service_widget.store + Yast::UI.CloseDialog +end + +def write + # intentionally nothing + true +end + +read +ui_loop +write + +nil diff --git a/library/systemd/examples/service_widget.rb b/library/systemd/examples/service_widget.rb new file mode 100644 index 000000000..c87c759ac --- /dev/null +++ b/library/systemd/examples/service_widget.rb @@ -0,0 +1,46 @@ +require "yast" + +require "yast2/service_widget" +require "yast2/service_configuration" + +Yast.import "SystemdService" + +def service + @service ||= Yast::SystemdService.find!("cups.service") +end + +def service_configuration + @service_configuration ||= Yast2::ServiceConfiguration.new(service) +end + +def service_widget + @service_widget ||= Yast2::ServiceWidget.new(service_configuration) +end + +def read + service_configuration.read +end + +include Yast::UIShortcuts + +def ui_loop + Yast::UI.OpenDialog(Yast::HBox(service_widget.content)) + loop do + input = Yast::UI.UserInput + service_widget.handle_input(input) + break if input == :cancel + end + service_widget.store + Yast::UI.CloseDialog +end + +def write + # intentionally nothing + true +end + +read +ui_loop +write + +nil diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 2b0a4cc23..34dacd434 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -14,8 +14,8 @@ module Yast2 # end # # def propose - # service_configuration.target_action = :restart - # service_configuration.target_autostart = :on_demand + # service_configuration.action = :restart + # service_configuration.autostart = :on_demand # end # # def show_dialog @@ -37,25 +37,125 @@ module Yast2 # end # end class ServiceWidget + include Yast::I18n + include Yast::Logger + include Yast::UIShortcuts # creates new widget instance for given service configuration - # @param services configuration holder - def initialize(service_configuratin) + # @param service_configuration [Yast2::ServiceConfiguration] configuration holder + def initialize(service_configuration) + textdomain "base" + @service_configuration = service_configuration end # gets widget term # @return def content + # TODO: disabling invalid action and autostart or kick it out? + Frame( + _("Service Configuration"), + VBox( + Left(HBox( + Label(_("Current status:")), + Label(" "), + Label(status) + )), + Left(action_widget), + Left(autostart_widget) + ) + ) end # handles event to dynamically react on user configuration. # For events that does not happen inside widget it is ignored. # @param event_id [Object] id of UI element that cause event + # @return [void] def handle_input(event_id) + nil end # Stores current configuration. Should be called always even when going # back so configuration is persistent when going again forward. def store + store_action + store_autostart + end + + private + + attr_reader :service_configuration + + def store_action + action = Yast::UI.QueryWidget(Id(:service_widget_action), :CurrentItem) + return unless action + + service_configuration.action = action.to_s.sub(/^service_widget_action_/, "").to_sym + end + + def store_autostart + autostart = Yast::UI.QueryWidget(Id(:service_widget_autostart), :CurrentItem) + return unless autostart + + service_configuration.autostart = autostart.to_s.sub(/^service_widget_autostart_/, "").to_sym + end + + def status + case service_configuration.status + # TRANSLATORS: Status of service + when :active + _("Active") + when :inactive + # TRANSLATORS: Status of service + _("Inactive") + when :inconsistent + # TRANSLATORS: Status of service + _("Partly Active") + when :unknown + # TRANSLATORS: Status of service + _("Unknown") + else + raise "Unknown status #{service_configuration.status.inspect}" + end + end + + def action_widget + ComboBox( + Id(:service_widget_action), + "After writting settings:", + action_items + ) + end + + def action_items + mixed = [:inconsistent, :unknown].include?(service_configuration.status) + restart_label = mixed ? _("Restart active and start inactive") : _("Restart") + reload_label = mixed ? _("Reload active and start inactive") : _("Reload") + current_action = service_configuration.action + [ + Item(Id(:service_widget_action_start), _("Ensure it is running"), current_action == :start), + Item(Id(:service_widget_action_stop), _("Ensure it is stopped"), current_action == :stop), + Item(Id(:service_widget_action_restart), restart_label, current_action == :restart), + Item(Id(:service_widget_action_reload), reload_label, current_action == :reload), + Item(Id(:service_widget_action_nothing), _("Nothing"), current_action == :nothing) + ] + end + + def autostart_widget + ComboBox( + Id(:service_widget_autostart), + "Start Service Automatically:", + autostart_items + ) + end + + def autostart_items + current_autostart = service_configuration.autostart + keep = [:inconsistent, :unknown].include?(current_autostart) + [ + Item(Id(:service_widget_autostart_on_boot), _("During boot"), current_autostart == :on_boot), + Item(Id(:service_widget_autostart_on_demand), _("On demand"), current_autostart == :on_demand), + Item(Id(:service_widget_autostart_manual), _("Never"), current_autostart == :manual), + Item(Id(:service_widget_autostart_inconsistent), _("Do not change"), keep), + ] end end end From de75bb5078063f6702ed245ac09da23e215d665e Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Mon, 2 Jul 2018 17:29:21 +0200 Subject: [PATCH 016/223] code review --- library/systemd/src/lib/yast2/service_configuration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb index d60fe408b..d3d8264ce 100644 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -135,7 +135,7 @@ def support_on_demand? private def read_status - services_active = @services.map { |s| s.active? } + services_active = @services.map(&:active?) return @status = :active if services_active.all? return @status = :inactive if services_active.none? From 7b06a49e15ca598528d14f3f462969fdd488eef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 2 Jul 2018 12:15:49 +0100 Subject: [PATCH 017/223] Delegate some SystemService to Yast::SystemdServiceClass::Service --- library/system/src/lib/yast2/system_service.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index b5a0b9332..f47f338ba 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -19,13 +19,19 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. +require "forwardable" Yast.import "SystemdService" module Yast2 class SystemService + extend Forwardable + # @return [Yast::SystemdService] attr_reader :service + # FIXME: temporary delegated methods. + def_delegators :@service, :running?, :start, :stop, :restart, :active?, :description + class << self def find(name) new(Yast::SystemdService.find(name)) @@ -42,7 +48,7 @@ def socket return @socket if @socket # not triggered - socket_name = properties.triggered_by + socket_name = service.properties.triggered_by return unless socket_name socket_name = socket_name[/\S+\.socket/] @@ -110,13 +116,5 @@ def start_modes @start_modes.insert(1, :on_demand) if socket? @start_modes end - - def start - raise NotImplementedError - end - - def stop - raise NotImplementedError - end end end From b2284fb55fb0372764738320e0f17efe1f1aeea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 2 Jul 2018 17:00:14 +0100 Subject: [PATCH 018/223] Add system_service.rb --- library/system/src/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/library/system/src/Makefile.am b/library/system/src/Makefile.am index 4a72f1e87..a11546f90 100644 --- a/library/system/src/Makefile.am +++ b/library/system/src/Makefile.am @@ -34,6 +34,7 @@ ylib_DATA = \ lib/yast2/fs_snapshot.rb \ lib/yast2/fs_snapshot_store.rb \ lib/yast2/target_file.rb \ + lib/yast2/system_service.rb \ lib/yast2/system_time.rb From 2a3770808a8833d1105fcc942027db5b7f730072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 2 Jul 2018 17:03:50 +0100 Subject: [PATCH 019/223] Add minimal documentation to Yast2::SystemService --- library/system/src/lib/yast2/system_service.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index f47f338ba..353a4f29f 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -23,13 +23,17 @@ Yast.import "SystemdService" module Yast2 + # This class represents a system service + # + # When talking about systemd, it might happen that a service is compose by a set of units + # (services, sockets, paths and so on). This class is able to group those units and + # offer an API to handle them together. class SystemService extend Forwardable # @return [Yast::SystemdService] attr_reader :service - # FIXME: temporary delegated methods. def_delegators :@service, :running?, :start, :stop, :restart, :active?, :description class << self From 45a8e69aebadb1943ffa0e4aa863f838ef5f1994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 3 Jul 2018 05:43:12 +0100 Subject: [PATCH 020/223] Document and test SystemdService.find --- library/system/src/lib/yast2/system_service.rb | 4 ++++ library/system/test/yast2/system_service_test.rb | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 353a4f29f..862217b7c 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -37,6 +37,10 @@ class SystemService def_delegators :@service, :running?, :start, :stop, :restart, :active?, :description class << self + # Find a service + # + # @param name [String] Service name + # @return [SystemService,nil] System service or nil when not found def find(name) new(Yast::SystemdService.find(name)) end diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 8ac5d420d..c1b67e9da 100644 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -33,6 +33,20 @@ allow(system_service).to receive(:socket).and_return(socket) end + describe ".find" do + let(:systemd_service) { instance_double(Yast::SystemdServiceClass::Service) } + + before do + allow(Yast::SystemdService).to receive(:find).with("cups").and_return(systemd_service) + end + + it "finds a systemd service" do + system_service = described_class.find("cups") + expect(system_service).to be_a(described_class) + expect(system_service.service).to eq(systemd_service) + end + end + describe "#start_mode" do context "when the service is enabled" do it "returns :on_boot" do From b92ebca5afc78da16955c5b45501b7acafdea11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 3 Jul 2018 05:43:29 +0100 Subject: [PATCH 021/223] Add a SystemdService.find_many finder --- .../system/src/lib/yast2/system_service.rb | 11 ++++++++ .../system/test/yast2/system_service_test.rb | 28 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 862217b7c..f03374299 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -44,6 +44,17 @@ class << self def find(name) new(Yast::SystemdService.find(name)) end + + # Find service names + # + # This method finds a set of system services. Currently it is just a wrapper around + # SystemdService.find_many. + # + # @param names [Array] Service names to find + # @return [Array] Found system services + def find_many(names) + Yast::SystemdService.find_many(names).compact.map { |s| new(s) } + end end # @param service [SystedService] diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index c1b67e9da..12704010c 100644 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -47,6 +47,34 @@ end end + describe ".find_many" do + let(:apparmor) { instance_double(Yast::SystemdServiceClass::Service) } + let(:cups) { instance_double(Yast::SystemdServiceClass::Service) } + + before do + allow(Yast::SystemdService).to receive(:find_many).with(["apparmor", "cups"]) + .and_return([apparmor, cups]) + end + + it "finds a set of systemd services" do + system_services = described_class.find_many(["apparmor", "cups"]) + expect(system_services).to be_all(Yast2::SystemService) + expect(system_services.map(&:service)).to eq([apparmor, cups]) + end + + context "when some service is not found" do + before do + allow(Yast::SystemdService).to receive(:find_many).with(["apparmor", "cups"]) + .and_return([nil, cups]) + end + + it "ignores the not found service" do + system_services = described_class.find_many(["apparmor", "cups"]) + expect(system_services.map(&:service)).to eq([cups]) + end + end + end + describe "#start_mode" do context "when the service is enabled" do it "returns :on_boot" do From 5373f7ddf158389bcdc8c8c09269406048068236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 3 Jul 2018 05:45:41 +0100 Subject: [PATCH 022/223] Add SystemService test to Makefile.am --- library/system/test/Makefile.am | 3 ++- library/system/test/yast2/system_service_test.rb | 0 2 files changed, 2 insertions(+), 1 deletion(-) mode change 100644 => 100755 library/system/test/yast2/system_service_test.rb diff --git a/library/system/test/Makefile.am b/library/system/test/Makefile.am index 3b54c3128..4f669eed1 100644 --- a/library/system/test/Makefile.am +++ b/library/system/test/Makefile.am @@ -4,7 +4,8 @@ TESTS = \ hw_detection_test.rb \ fs_snapshot_test.rb \ fs_snapshot_store_test.rb \ - proc_cmdline_test.rb + proc_cmdline_test.rb \ + yast2/system_service_test.rb TEST_EXTENSIONS = .rb RB_LOG_COMPILER = rspec diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb old mode 100644 new mode 100755 From 0e382b2042b4d65a44b8b12d48ecb7f5f31ac73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 3 Jul 2018 07:14:11 +0100 Subject: [PATCH 023/223] Simplify SystemService#start_modes --- library/system/src/lib/yast2/system_service.rb | 2 +- library/system/test/yast2/system_service_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index f03374299..438f4a993 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -132,7 +132,7 @@ def start_mode=(mode) def start_modes return @start_modes if @start_modes @start_modes = [:on_boot, :manual] - @start_modes.insert(1, :on_demand) if socket? + @start_modes << :on_demand if socket? @start_modes end end diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 12704010c..c4fe2924b 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -152,7 +152,7 @@ let(:socket) { double("socket", disable: true) } it "returns :on_boot, :on_demand and :manual" do - expect(system_service.start_modes).to eq([:on_boot, :on_demand, :manual]) + expect(system_service.start_modes).to eq([:on_boot, :manual, :on_demand]) end end From bf10ff34cb36147e2c2916fae5e76685de892370 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 3 Jul 2018 10:18:52 +0200 Subject: [PATCH 024/223] make rubocop happy --- .../src/lib/yast2/service_configuration.rb | 26 +++++++++---------- .../systemd/src/lib/yast2/service_widget.rb | 22 +++++++++------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb index d3d8264ce..7f2e55856 100644 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -79,11 +79,11 @@ def action @action end - ACTIONS = [ :start, :stop, :reload, :restart, :nothing].freeze + ACTIONS = [:start, :stop, :reload, :restart, :nothing].freeze # sets target action for services # @param action[:start, :stop, :restart, :reload, :nothing] for possible values and # its explanation please see return value of {target_action} - def action= (action) + def action=(action) if !ACTIONS.include?(action) raise ArgumentError, "Invalid parameter #{action.inspect}" end @@ -108,7 +108,7 @@ def autostart @autostart end - AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent] + AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent].freeze # sets autostart configuration. # @param [:on_boot, :on_demand, :manual, :inconsistent] autostart # configuratio. For explanation please see {autostart} when @@ -148,16 +148,16 @@ def read_status def read_autostart sockets = @services.map(&:socket).compact services_without_socket = @services.reject(&:socket) - if sockets.all?(&:enabled?) && services_without_socket.all?(&:enabled?) - @autostart = :on_demand + @autostart = if sockets.all?(&:enabled?) && services_without_socket.all?(&:enabled?) + :on_demand elsif @services.all?(&:enabled?) - @autostart = :on_boot + :on_boot elsif sockets.none?(&:enabled?) && @services.none?(&:enabled?) - @autostart = :manual + :manual else - @autostart = :inconsistent + :inconsistent end - rescue SystemctlError =>e + rescue SystemctlError => e log.error "systemctl failure: #{e.inspect}" @autostart = :unknown end @@ -166,11 +166,11 @@ def write_autostart case autostart when :on_boot then @services.each { |s| s.start_mode = :boot } when :on_demand - @services.each do |service| - if s.start_modes.include?(:demand) - s.start_mode = :demand + @services.each do |_service| + s.start_mode = if s.start_modes.include?(:demand) + :demand else - s.start_mode = :boot + :boot end end when :manual then @services.each { |s| s.start_mode = :manual } diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 34dacd434..2e6006c6e 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -54,11 +54,13 @@ def content Frame( _("Service Configuration"), VBox( - Left(HBox( - Label(_("Current status:")), - Label(" "), - Label(status) - )), + Left( + HBox( + Label(_("Current status:")), + Label(" "), + Label(status) + ) + ), Left(action_widget), Left(autostart_widget) ) @@ -70,6 +72,8 @@ def content # @param event_id [Object] id of UI element that cause event # @return [void] def handle_input(event_id) + log.info "handle event #{event_id}" + nil end @@ -104,13 +108,13 @@ def status when :active _("Active") when :inactive - # TRANSLATORS: Status of service + # TRANSLATORS: Status of service _("Inactive") when :inconsistent - # TRANSLATORS: Status of service + # TRANSLATORS: Status of service _("Partly Active") when :unknown - # TRANSLATORS: Status of service + # TRANSLATORS: Status of service _("Unknown") else raise "Unknown status #{service_configuration.status.inspect}" @@ -154,7 +158,7 @@ def autostart_items Item(Id(:service_widget_autostart_on_boot), _("During boot"), current_autostart == :on_boot), Item(Id(:service_widget_autostart_on_demand), _("On demand"), current_autostart == :on_demand), Item(Id(:service_widget_autostart_manual), _("Never"), current_autostart == :manual), - Item(Id(:service_widget_autostart_inconsistent), _("Do not change"), keep), + Item(Id(:service_widget_autostart_inconsistent), _("Do not change"), keep) ] end end From ed58840f4221d78976bceb4984f64eb96379b8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 3 Jul 2018 14:47:38 +0100 Subject: [PATCH 025/223] Fix SystemService documentation --- library/system/src/lib/yast2/system_service.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 438f4a993..c568c644d 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -57,12 +57,14 @@ def find_many(names) end end - # @param service [SystedService] + # @param service [Yast::SystemdServiceClass::Service] def initialize(service) @service = service end # Returns socket associated with service or nil if there is no such socket + # + # @return [Yast::SystemdSocketClass::Socket] def socket return @socket if @socket @@ -104,6 +106,7 @@ def start_mode # :manual and, in some cases, :on_demand). # # @see #start_modes + # @raise ArgumentError when mode is not valid def start_mode=(mode) if !start_modes.include?(mode) raise ArgumentError, "Invalid start mode: '#{mode}' for service '#{service.name}'" From 713ddfad4988ff9feb7562057a789191d036d0ce Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 3 Jul 2018 16:36:27 +0200 Subject: [PATCH 026/223] Add test for configuration --- .../src/lib/yast2/service_configuration.rb | 6 +- .../test/yast2/service_configuration.rb | 106 ++++++++++++++++++ 2 files changed, 109 insertions(+), 3 deletions(-) create mode 100755 library/systemd/test/yast2/service_configuration.rb diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb index 7f2e55856..f5b240fa4 100644 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -157,7 +157,7 @@ def read_autostart else :inconsistent end - rescue SystemctlError => e + rescue Yast::SystemctlError => e log.error "systemctl failure: #{e.inspect}" @autostart = :unknown end @@ -166,8 +166,8 @@ def write_autostart case autostart when :on_boot then @services.each { |s| s.start_mode = :boot } when :on_demand - @services.each do |_service| - s.start_mode = if s.start_modes.include?(:demand) + @services.each do |service| + service.start_mode = if service.start_modes.include?(:demand) :demand else :boot diff --git a/library/systemd/test/yast2/service_configuration.rb b/library/systemd/test/yast2/service_configuration.rb new file mode 100755 index 000000000..d6c713ebd --- /dev/null +++ b/library/systemd/test/yast2/service_configuration.rb @@ -0,0 +1,106 @@ +#!/usr/bin/env rspec + +require_relative "../test_helper" + +require "yast2/service_configuration" + +describe Yast2::ServiceConfiguration do + describe ".new" do + it "raises ArgumentError if non service is passed" do + expect{described_class.new(nil)}.to raise_error(ArgumentError) + end + end + + describe "#read" do + context "reading status" do + it "sets status to :active if all passed services is active" do + service1 = double(active?: true, is_a?: true).as_null_object + service2 = double(active?: true, is_a?: true).as_null_object + service_configuration = described_class.new(service1, service2) + + service_configuration.read + expect(service_configuration.status).to eq :active + end + + it "sets status to :inactive if all passed services is not active" do + service1 = double(active?: false, is_a?: true).as_null_object + service2 = double(active?: false, is_a?: true).as_null_object + service_configuration = described_class.new(service1, service2) + + service_configuration.read + expect(service_configuration.status).to eq :inactive + end + + it "sets status to :inconsistent if only some services is active" do + service1 = double(active?: false, is_a?: true).as_null_object + service2 = double(active?: true, is_a?: true).as_null_object + service_configuration = described_class.new(service1, service2) + + service_configuration.read + expect(service_configuration.status).to eq :inconsistent + end + end + + context "reading auto start" do + # TODO: write after start using startmode + end + end + + describe "#write" do + context "writting action" do + # TODO: it should delegate this logic with sockets to systemservice + it "calls start if action is :start" do + service1 = double(is_a?: true).as_null_object + socket1 = double.as_null_object + service2 = double(is_a?: true, socket: socket1).as_null_object + service_configuration = described_class.new(service1, service2) + + service_configuration.action = :start + expect(service1).to receive(:start) + expect(service2).to_not receive(:start) + expect(socket1).to receive(:start) + service_configuration.write + end + + it "calls stop if action is :stop" do + service1 = double(is_a?: true).as_null_object + service_configuration = described_class.new(service1) + + service_configuration.action = :stop + expect(service1).to receive(:stop) + service_configuration.write + end + + it "calls restart if action is :restart" do + service1 = double(is_a?: true).as_null_object + service_configuration = described_class.new(service1) + + service_configuration.action = :restart + expect(service1).to receive(:restart) + service_configuration.write + end + + it "calls reload or restart if action is :reload" do + service1 = double(is_a?: true).as_null_object + service_configuration = described_class.new(service1) + + service_configuration.action = :reload + expect(service1).to receive(:reload_or_restart) + service_configuration.write + end + + it "does nothing if action is :nothing" do + service1 = double(is_a?: true, active?: false, socket: nil, enabled?: false, "start_mode=": nil) + service_configuration = described_class.new(service1) + + service_configuration.action = :nothing + service_configuration.write + end + end + + context "writting auto start" do + # TODO: write after start using start_mode in system service + end + + end +end From c5499a707142aa31247a1a3b41dc2da8e555a56c Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 3 Jul 2018 17:12:57 +0200 Subject: [PATCH 027/223] fix rubocop --- .rubocop.yml | 4 ++++ library/systemd/test/yast2/service_configuration.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index fe1bf0880..6896c438f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -34,6 +34,10 @@ Style/AccessorMethodName: Exclude: - library/systemd/src/modules/systemd_target.rb +# set reasonable ruby version +AllCops: + TargetRubyVersion: 2.2 + # UI_ID module is not camel case Style/ClassAndModuleCamelCase: Enabled: false diff --git a/library/systemd/test/yast2/service_configuration.rb b/library/systemd/test/yast2/service_configuration.rb index d6c713ebd..bc4cfab9d 100755 --- a/library/systemd/test/yast2/service_configuration.rb +++ b/library/systemd/test/yast2/service_configuration.rb @@ -7,7 +7,7 @@ describe Yast2::ServiceConfiguration do describe ".new" do it "raises ArgumentError if non service is passed" do - expect{described_class.new(nil)}.to raise_error(ArgumentError) + expect { described_class.new(nil) }.to raise_error(ArgumentError) end end From 7410c2ccbc7d3fc58e04de986ebf14ac2c76c001 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 3 Jul 2018 17:28:41 +0200 Subject: [PATCH 028/223] try to fix old makefile --- library/systemd/test/yast2/Makefile.am | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 library/systemd/test/yast2/Makefile.am diff --git a/library/systemd/test/yast2/Makefile.am b/library/systemd/test/yast2/Makefile.am new file mode 100644 index 000000000..1a2d476a1 --- /dev/null +++ b/library/systemd/test/yast2/Makefile.am @@ -0,0 +1,9 @@ +TESTS = \ + yast2/service_configuration_test.rb + +TEST_EXTENSIONS = .rb +RB_LOG_COMPILER = rspec +VERBOSE = 1 + +EXTRA_DIST = $(TESTS) + From 6d88e21dbfec5e63111bb3486591279f0cb0d2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 4 Jul 2018 08:13:41 +0100 Subject: [PATCH 029/223] Add methods to read the service state - SystemService#active_state - SystemService#sub_state --- library/system/src/lib/yast2/system_service.rb | 3 ++- library/systemd/src/lib/yast2/systemd_unit.rb | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index c568c644d..a92c35dde 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -34,7 +34,8 @@ class SystemService # @return [Yast::SystemdService] attr_reader :service - def_delegators :@service, :running?, :start, :stop, :restart, :active?, :description + def_delegators :@service, :running?, :start, :stop, :restart, :active?, + :active_state, :sub_state, :description class << self # Find a service diff --git a/library/systemd/src/lib/yast2/systemd_unit.rb b/library/systemd/src/lib/yast2/systemd_unit.rb index c358b0d2f..4fa1397d0 100644 --- a/library/systemd/src/lib/yast2/systemd_unit.rb +++ b/library/systemd/src/lib/yast2/systemd_unit.rb @@ -72,6 +72,7 @@ class PropMap < Hash # with ruby 2.4 delegating ostruct with Forwardable start to write warning # so define it manually (bsc#1049433) +<<<<<<< HEAD FORWARDED_METHODS = [ :id, :path, @@ -87,6 +88,11 @@ class PropMap < Hash private_constant :FORWARDED_METHODS FORWARDED_METHODS.each { |m| define_method(m) { properties.public_send(m) } } +======= + [:id, :path, :description, :active?, :enabled?, :loaded?, :active_state, :sub_state].each do |m| + define_method(m) { properties.public_send(m) } + end +>>>>>>> Add methods to read the service state # @return [String] eg. "apache2" # (the canonical one, may be different from unit_name) From 4677bf693a5c3f0d17344291dbc07da0759c25cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 4 Jul 2018 16:00:33 +0100 Subject: [PATCH 030/223] Add missing ServiceConfiguration to makefiles --- library/systemd/src/Makefile.am | 1 + library/systemd/test/Makefile.am | 1 + .../{service_configuration.rb => service_configuration_test.rb} | 0 3 files changed, 2 insertions(+) rename library/systemd/test/yast2/{service_configuration.rb => service_configuration_test.rb} (100%) diff --git a/library/systemd/src/Makefile.am b/library/systemd/src/Makefile.am index 1fe12fad6..af5cea22c 100644 --- a/library/systemd/src/Makefile.am +++ b/library/systemd/src/Makefile.am @@ -5,6 +5,7 @@ module_DATA = \ ylibdir = @ylibdir@/yast2 ylib_DATA = \ + lib/yast2/service_configuration.rb \ lib/yast2/systemctl.rb \ lib/yast2/systemd_unit.rb \ lib/yast2/system_service.rb diff --git a/library/systemd/test/Makefile.am b/library/systemd/test/Makefile.am index e5435fa14..f523901b3 100644 --- a/library/systemd/test/Makefile.am +++ b/library/systemd/test/Makefile.am @@ -1,4 +1,5 @@ TESTS = \ + yast2/service_configuration_test.rb \ systemctl_test.rb \ systemd_unit_test.rb \ systemd_socket_test.rb \ diff --git a/library/systemd/test/yast2/service_configuration.rb b/library/systemd/test/yast2/service_configuration_test.rb similarity index 100% rename from library/systemd/test/yast2/service_configuration.rb rename to library/systemd/test/yast2/service_configuration_test.rb From 444aba08e29ef9dcc80bc7abc6764b3dca206a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 5 Jul 2018 10:00:28 +0100 Subject: [PATCH 031/223] Adapt SystemService to keep changes in memory --- .../system/src/lib/yast2/system_service.rb | 129 +++++++++-- .../system/test/yast2/system_service_test.rb | 216 ++++++++++++++++-- 2 files changed, 304 insertions(+), 41 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index a92c35dde..f639c19d2 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -28,6 +28,24 @@ module Yast2 # When talking about systemd, it might happen that a service is compose by a set of units # (services, sockets, paths and so on). This class is able to group those units and # offer an API to handle them together. + # + # Additionally, this class does not modify the underlying system until the #save + # method is called. + # + # @example Enabling a service + # cups = SystemService.find("cups") + # cups.start_mode = :boot + # cups.save + # + # @example Activating a service + # cups = SystemService.find("cups") + # cups.active = true + # cups.save + # + # @example Ignoring status changes on 1st stage + # cups = SystemService.find("cups") + # cups.active = true + # cups.save(ignore_status: true) class SystemService extend Forwardable @@ -35,7 +53,7 @@ class SystemService attr_reader :service def_delegators :@service, :running?, :start, :stop, :restart, :active?, - :active_state, :sub_state, :description + :active_state, :sub_state, :name, :description class << self # Find a service @@ -61,6 +79,7 @@ def find_many(names) # @param service [Yast::SystemdServiceClass::Service] def initialize(service) @service = service + @changes = {} end # Returns socket associated with service or nil if there is no such socket @@ -79,14 +98,14 @@ def socket @socket = Yast::SystemdSocket.find(socket_name) end - # Determine whether the service has an associated socket + # Determines whether the service has an associated socket # # @return [Boolean] true if an associated socket exists; false otherwise. def socket? !socket.nil? end - # Return the start mode + # Returns the start mode # # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, # :manual and, in some cases, :on_demand). @@ -96,15 +115,13 @@ def socket? # # @return [Symbol] Start mode (:on_boot, :on_demand, :manual) def start_mode - return :on_boot if service.enabled? - return :on_demand if socket && socket.enabled? - :manual + changes[:start_mode] || current_start_mode end - # Set the service start mode + # Sets the service start mode # # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, - # :manual and, in some cases, :on_demand). + # :manual and, in some cases, :on_demand). The given value will be applied after calling #save. # # @see #start_modes # @raise ArgumentError when mode is not valid @@ -113,19 +130,53 @@ def start_mode=(mode) raise ArgumentError, "Invalid start mode: '#{mode}' for service '#{service.name}'" end - case mode - when :on_boot - service.enable - socket.disable - when :on_demand - service.disable - socket.enable - when :manual - service.disable - socket.disable + if mode == current_start_mode + changes.delete(:start_mode) + else + changes[:start_mode] = mode end end + # Sets whether the service should be active or not + # + # The given value will be applied after calling #save. + # + # @param value [Boolean] true to set this service as active + def active=(active) + if active == service.active? + changes.delete(:active) + else + changes[:active] = active + end + end + + def active + return changes[:active] unless changes[:active].nil? + service.active? + end + + # Saves changes to the underlying system + # + # @param set_status [Boolean] Do not change service status. Useful when running on 1st stage. + def save(ignore_status: false) + save_start_mode + set_current_status unless ignore_status + reload + end + + # Reverts stored changes + def reload + changes.clear + @current_start_mode = nil + end + + # Determines whether the system has been changed or not + # + # @return [Boolean] true if the system has been changed + def changed? + !changes.empty? + end + # Return the list of supported start modes # # * :on_boot: The service will be started when the system boots. @@ -139,5 +190,47 @@ def start_modes @start_modes << :on_demand if socket? @start_modes end + + private + + # @return [Hash] + attr_reader :changes + + # Get the current start_mode + def current_start_mode + @current_start_mode ||= + if service.enabled? + :on_boot + elsif socket && socket.enabled? + :on_demand + else + :manual + end + end + + # Sets start mode to the underlying system + def save_start_mode + return unless changes[:start_mode] + case changes[:start_mode] + when :on_boot + service.enable + socket.disable + when :on_demand + service.disable + socket.enable + when :manual + service.disable + socket.disable + end + end + + # Sets service status + def set_current_status + if changes[:active] && !service.active? + service.start + elsif changes[:active] == false && service.active? + service.stop + end + end end end diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index c4fe2924b..79bf1e661 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -26,8 +26,9 @@ describe Yast2::SystemService do subject(:system_service) { described_class.new(service) } - let(:service) { double("service", enabled?: true, name: "cups") } + let(:service) { double("service", enabled?: true, name: "cups", active?: active?, enable: nil) } let(:socket) { double("socket", enabled?: true) } + let(:active?) { true } before do allow(system_service).to receive(:socket).and_return(socket) @@ -110,30 +111,13 @@ end describe "#start_mode=" do - let(:socket) { double("socket", disable: true) } - - context "when :on_boot mode is given" do - it "enables the service to start on boot" do - expect(service).to receive(:enable) - expect(socket).to receive(:disable) - system_service.start_mode = :on_boot - end - end - - context "when :on_demand mode is given" do - it "enables the socket" do - expect(service).to receive(:disable) - expect(socket).to receive(:enable) - system_service.start_mode = :on_demand - end + before do + allow(service).to receive(:enable) end - context "when :manual mode is given" do - it "disables the service and the socket" do - expect(service).to receive(:disable) - expect(socket).to receive(:disable) - system_service.start_mode = :manual - end + it "sets the wanted start_mode" do + expect { system_service.start_mode = :on_demand }.to change { system_service.start_mode } + .from(:on_boot).to(:on_demand) end context "when an invalid value is given" do @@ -141,6 +125,13 @@ expect { system_service.start_mode = :other }.to raise_error(ArgumentError) end end + + context "when the wanted start_mode is the same than the current one" do + it "ignores the change" do + system_service.start_mode = :on_boot + expect(system_service.changed?).to eq(false) + end + end end describe "#start_modes" do @@ -165,6 +156,185 @@ end end + describe "#active=" do + context "when set to true" do + let(:active?) { false } + + it "sets #active to true" do + expect { system_service.active = true }.to change { system_service.active } + .from(false).to(true) + end + + context "and the service is already active" do + let(:active?) { true } + + it "ignores the change" do + system_service.active = true + expect(system_service.changed?).to eq(false) + end + end + end + + context "when set to false" do + let(:active?) { true } + + it "sets #active to false" do + expect { system_service.active = false }.to change { system_service.active } + .from(true).to(false) + end + + context "and the service is already inactive" do + let(:active?) { false } + + it "ignores the change" do + system_service.active = false + expect(system_service.changed?).to eq(false) + end + end + end + end + + describe "#active" do + context "when service is active" do + let(:active?) { true } + + it "returns true" do + expect(system_service.active).to eq(true) + end + end + + context "when service is inactive" do + let(:active?) { false } + + it "returns false" do + expect(system_service.active).to eq(false) + end + end + + context "when an active value was given" do + before do + system_service.active = false + end + + it "returns the given value" do + expect(system_service.active).to eq(false) + end + end + end + + describe "#save=" do + let(:socket) { double("socket", disable: true) } + let(:start_mode) { :on_boot } + + before do + system_service.start_mode = start_mode + end + + context "when start_mode was changed to :on_boot" do + let(:service) { double("service", enabled?: false, name: "cups") } + let(:socket) { double("socket", enabled?: true) } + let(:start_mode) { :on_boot } + + it "enables the service to start on boot" do + expect(service).to receive(:enable) + expect(socket).to receive(:disable) + system_service.save + end + end + + context "when start_mode was changed to :on_demand" do + let(:start_mode) { :on_demand } + + it "enables the socket" do + expect(service).to receive(:disable) + expect(socket).to receive(:enable) + system_service.save + end + end + + context "when start_mode was changed to :manual" do + let(:start_mode) { :manual } + + it "disables the service and the socket" do + expect(service).to receive(:disable) + expect(socket).to receive(:disable) + system_service.save + end + end + + context "when active is set to true" do + before { system_service.active = true } + + context "and the service is already active" do + let(:active?) { true } + + it "does not try to activate the service again" do + expect(service).to_not receive(:start) + system_service.save + end + end + + context "and the service is inactive" do + let(:active?) { false } + + it "tries to activate the service" do + expect(service).to receive(:start) + system_service.save + end + + it "does not active the service if the status must be ignored" do + expect(service).to_not receive(:start) + system_service.save(ignore_status: true) + end + end + end + + context "when active is set to false" do + before { system_service.active = false } + + context "and the service is active" do + let(:active?) { true } + + it "tries to stop the service" do + expect(service).to receive(:stop) + system_service.save + end + + it "does not stop the service if the status must be ignored" do + expect(service).to_not receive(:start) + system_service.save(ignore_status: true) + end + end + + context "and the service is inactive" do + let(:active?) { false } + + it "does not try to stop the service again" do + expect(service).to_not receive(:stop) + system_service.save + end + end + end + end + + describe "#changed?" do + context "when some change was made" do + before do + system_service.active = false + end + + it "returns true" do + expect(system_service.changed?).to eq(true) + end + end + + context "when no changes were made" do + it "returns false" do + expect(system_service.changed?).to eq(false) + end + end + end + describe "#socket?" do before do allow(system_service).to receive(:socket).and_return(socket) From 7ac76d00d64d407db9e17d787174731d6c9cd13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 5 Jul 2018 11:58:34 +0100 Subject: [PATCH 032/223] Move SystemService#socket method to SystemdService class --- .../system/src/lib/yast2/system_service.rb | 32 +++++-------------- .../system/test/yast2/system_service_test.rb | 28 +++------------- 2 files changed, 12 insertions(+), 48 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index f639c19d2..bc86c26d1 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -82,29 +82,6 @@ def initialize(service) @changes = {} end - # Returns socket associated with service or nil if there is no such socket - # - # @return [Yast::SystemdSocketClass::Socket] - def socket - return @socket if @socket - - # not triggered - socket_name = service.properties.triggered_by - return unless socket_name - - socket_name = socket_name[/\S+\.socket/] - return unless socket_name # triggered by non-socket - - @socket = Yast::SystemdSocket.find(socket_name) - end - - # Determines whether the service has an associated socket - # - # @return [Boolean] true if an associated socket exists; false otherwise. - def socket? - !socket.nil? - end - # Returns the start mode # # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, @@ -187,7 +164,7 @@ def changed? def start_modes return @start_modes if @start_modes @start_modes = [:on_boot, :manual] - @start_modes << :on_demand if socket? + @start_modes << :on_demand if socket @start_modes end @@ -232,5 +209,12 @@ def set_current_status service.stop end end + + # Returns the associated socket + # + # @return [Yast::SystemdSocketClass::Socket] + def socket + service && service.socket + end end end diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 79bf1e661..97365ffa5 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -26,12 +26,14 @@ describe Yast2::SystemService do subject(:system_service) { described_class.new(service) } - let(:service) { double("service", enabled?: true, name: "cups", active?: active?, enable: nil) } + let(:service) do + double("service", enabled?: true, name: "cups", active?: active?, enable: nil) + end let(:socket) { double("socket", enabled?: true) } let(:active?) { true } before do - allow(system_service).to receive(:socket).and_return(socket) + allow(service).to receive(:socket).and_return(socket) end describe ".find" do @@ -334,26 +336,4 @@ end end end - - describe "#socket?" do - before do - allow(system_service).to receive(:socket).and_return(socket) - end - - context "when there is an associated socket" do - let(:socket) { double("socket") } - - it "returns true" do - expect(system_service.socket?).to eq(true) - end - end - - context "when there is no associated socket" do - let(:socket) { nil } - - it "returns false" do - expect(system_service.socket?).to eq(false) - end - end - end end From 71179054c1648ba3aa3264a3dfebc4975f9a94a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Fri, 6 Jul 2018 10:57:03 +0100 Subject: [PATCH 033/223] Add more agnostic method names --- library/system/src/lib/yast2/system_service.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index bc86c26d1..d80fdd913 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -52,8 +52,17 @@ class SystemService # @return [Yast::SystemdService] attr_reader :service - def_delegators :@service, :running?, :start, :stop, :restart, :active?, - :active_state, :sub_state, :name, :description + def_delegators :@service, :running?, :start, :stop, :restart, :active?, :name, :description + + # @!method state + # + # @return [String] + def_delegator :@service, :active_state, :state + + # @!method substate + # + # @return [String] + def_delegator :@service, :sub_state, :substate class << self # Find a service From 2f693cc6b6a5c816f54899eda7267592361a2f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Fri, 6 Jul 2018 10:58:20 +0100 Subject: [PATCH 034/223] Add method SystemService#search_terms --- .../system/src/lib/yast2/system_service.rb | 16 ++++++++++++-- .../system/test/yast2/system_service_test.rb | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index d80fdd913..d7a7b251c 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -73,7 +73,7 @@ def find(name) new(Yast::SystemdService.find(name)) end - # Find service names + # Finds service names # # This method finds a set of system services. Currently it is just a wrapper around # SystemdService.find_many. @@ -163,7 +163,7 @@ def changed? !changes.empty? end - # Return the list of supported start modes + # Returns the list of supported start modes # # * :on_boot: The service will be started when the system boots. # * :manual: The service is disabled and it will be started manually. @@ -177,6 +177,18 @@ def start_modes @start_modes end + # Terms to search for this service + # + # In case the service has an associated socket, the socket name + # is included as search term. + # + # @return [Array] e.g., #=> ["tftp.service", "tftp.socket"] + def search_terms + terms = [service.id] + terms << socket.id if socket + terms + end + private # @return [Hash] diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 97365ffa5..1988e6a47 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -336,4 +336,26 @@ end end end + + describe "#search_terms" do + before do + allow(service).to receive(:id).and_return("cups.service") + end + + context "when the service has not associated socket" do + let(:socket) { nil } + + it "returns only the service full name" do + expect(system_service.search_terms).to contain_exactly("cups.service") + end + end + + context "when the service has an associated socket" do + let(:socket) { instance_double(Yast::SystemdSocketClass::Socket, id: "cups.socket") } + + it "returns the service and socket full names" do + expect(system_service.search_terms).to contain_exactly("cups.service", "cups.socket") + end + end + end end From 8cc4f7c6ce303e2b4032186bde91b6468eb3b9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Fri, 6 Jul 2018 11:18:58 +0100 Subject: [PATCH 035/223] Fix text --- library/system/test/yast2/system_service_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 1988e6a47..a7a31e1fd 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -342,7 +342,7 @@ allow(service).to receive(:id).and_return("cups.service") end - context "when the service has not associated socket" do + context "when the service does not have an associated socket" do let(:socket) { nil } it "returns only the service full name" do From ab72d2d086d65a0fff68f4545a952657ed8a27fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 12:18:01 +0100 Subject: [PATCH 036/223] Alias SystemService#active? to SystemService#active --- library/system/src/lib/yast2/system_service.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index d7a7b251c..7128be065 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -52,7 +52,7 @@ class SystemService # @return [Yast::SystemdService] attr_reader :service - def_delegators :@service, :running?, :start, :stop, :restart, :active?, :name, :description + def_delegators :@service, :running?, :start, :stop, :restart, :name, :description # @!method state # @@ -136,11 +136,16 @@ def active=(active) end end + # Determine whether the service will be active after calling #save + # + # @return [Boolean] true if the service must be active; false otherwise def active return changes[:active] unless changes[:active].nil? service.active? end + alias_method :active?, :active + # Saves changes to the underlying system # # @param set_status [Boolean] Do not change service status. Useful when running on 1st stage. From d150cdf3e23cd0f65b016e6d37ddb4031d006b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 12:25:49 +0100 Subject: [PATCH 037/223] Add SystemService#changed_value? --- .../system/src/lib/yast2/system_service.rb | 58 ++++++++++++++----- .../system/test/yast2/system_service_test.rb | 18 ++++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 7128be065..f2518077e 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -49,6 +49,8 @@ module Yast2 class SystemService extend Forwardable + Change = Struct.new(:old_value, :new_value) + # @return [Yast::SystemdService] attr_reader :service @@ -101,7 +103,7 @@ def initialize(service) # # @return [Symbol] Start mode (:on_boot, :on_demand, :manual) def start_mode - changes[:start_mode] || current_start_mode + new_value_for(:start_mode) || current_start_mode end # Sets the service start mode @@ -117,9 +119,9 @@ def start_mode=(mode) end if mode == current_start_mode - changes.delete(:start_mode) + unregister_change(:start_mode) else - changes[:start_mode] = mode + register_change(:start_mode, current_start_mode, mode) end end @@ -128,11 +130,11 @@ def start_mode=(mode) # The given value will be applied after calling #save. # # @param value [Boolean] true to set this service as active - def active=(active) - if active == service.active? - changes.delete(:active) + def active=(value) + if value == service.active? + unregister_change(:start_mode) else - changes[:active] = active + register_change(:active, service.active?, value) end end @@ -140,7 +142,8 @@ def active=(active) # # @return [Boolean] true if the service must be active; false otherwise def active - return changes[:active] unless changes[:active].nil? + new_value = new_value_for(:active) + return new_value unless new_value.nil? service.active? end @@ -157,7 +160,7 @@ def save(ignore_status: false) # Reverts stored changes def reload - changes.clear + clear_changes @current_start_mode = nil end @@ -194,6 +197,13 @@ def search_terms terms end + # Determines whether a value has been changed + # + # @return [Boolean] true if the value has been changed; false otherwise + def changed_value?(key) + changes.key?(key) + end + private # @return [Hash] @@ -213,8 +223,8 @@ def current_start_mode # Sets start mode to the underlying system def save_start_mode - return unless changes[:start_mode] - case changes[:start_mode] + return unless new_value_for(:start_mode) + case new_value_for(:start_mode) when :on_boot service.enable socket.disable @@ -229,9 +239,9 @@ def save_start_mode # Sets service status def set_current_status - if changes[:active] && !service.active? + if new_value_for(:active) && !service.active? service.start - elsif changes[:active] == false && service.active? + elsif new_value_for(:active) == false && service.active? service.stop end end @@ -242,5 +252,27 @@ def set_current_status def socket service && service.socket end + + def unregister_change(key) + changes.delete(key) + end + + def register_change(key, old_value, new_value) + changes[key] = Change.new(old_value, new_value) + end + + def clear_changes + changes.clear + end + + def new_value_for(key) + return nil unless changes[key] + changes[key].new_value + end + + def old_value_for(key) + return nil unless changes[key] + changes[key].old_value + end end end diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index a7a31e1fd..b8661f730 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -358,4 +358,22 @@ end end end + + describe "#changed_value?" do + context "when no value has been changed" do + it "returns true" do + expect(system_service.changed_value?(:active)).to eq(false) + end + end + + context "when some value has been changed" do + before do + system_service.active = !system_service.active + end + + it "returns true" do + expect(system_service.changed_value?(:active)).to eq(true) + end + end + end end From 2cfc39fdf2b94c8c683469e2e5ad63ea982798c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 12:40:51 +0100 Subject: [PATCH 038/223] Simplify SystemService changes implementation --- .../system/src/lib/yast2/system_service.rb | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index f2518077e..7eb4728ef 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -49,8 +49,6 @@ module Yast2 class SystemService extend Forwardable - Change = Struct.new(:old_value, :new_value) - # @return [Yast::SystemdService] attr_reader :service @@ -121,7 +119,7 @@ def start_mode=(mode) if mode == current_start_mode unregister_change(:start_mode) else - register_change(:start_mode, current_start_mode, mode) + register_change(:start_mode, mode) end end @@ -134,7 +132,7 @@ def active=(value) if value == service.active? unregister_change(:start_mode) else - register_change(:active, service.active?, value) + register_change(:active, value) end end @@ -142,8 +140,7 @@ def active=(value) # # @return [Boolean] true if the service must be active; false otherwise def active - new_value = new_value_for(:active) - return new_value unless new_value.nil? + return new_value_for(:active) if changed_value?(:active) service.active? end @@ -223,7 +220,7 @@ def current_start_mode # Sets start mode to the underlying system def save_start_mode - return unless new_value_for(:start_mode) + return unless changed_value?(:start_mode) case new_value_for(:start_mode) when :on_boot service.enable @@ -253,26 +250,33 @@ def socket service && service.socket end + # Unregisters change for a given key + # + # @param [Symbol] Change key def unregister_change(key) changes.delete(key) end - def register_change(key, old_value, new_value) - changes[key] = Change.new(old_value, new_value) + # Registers change for a given key + # + # @param [Symbol] Change key + # @param [Object] New value + def register_change(key, new_value) + changes[key] = new_value end + # Clears changes def clear_changes changes.clear end + # Returns the new value for a given key + # + # @param [Symbol] Change key + # @return [Object] New value def new_value_for(key) - return nil unless changes[key] - changes[key].new_value - end - - def old_value_for(key) - return nil unless changes[key] - changes[key].old_value + return nil unless changed_value?(key) + changes[key] end end end From 614f913b4f9db97b877a6807826559500237711d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 13:28:00 +0100 Subject: [PATCH 039/223] Fix SystemService#active= method --- library/system/src/lib/yast2/system_service.rb | 4 ++-- library/system/test/yast2/system_service_test.rb | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 7eb4728ef..209e6d4ac 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -129,8 +129,8 @@ def start_mode=(mode) # # @param value [Boolean] true to set this service as active def active=(value) - if value == service.active? - unregister_change(:start_mode) + if value == active? + unregister_change(:active) else register_change(:active, value) end diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index b8661f730..5d4b479a8 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -129,6 +129,10 @@ end context "when the wanted start_mode is the same than the current one" do + before do + system_service.start_mode = :on_demand + end + it "ignores the change" do system_service.start_mode = :on_boot expect(system_service.changed?).to eq(false) @@ -168,7 +172,9 @@ end context "and the service is already active" do - let(:active?) { true } + before do + system_service.active = true + end it "ignores the change" do system_service.active = true From 9774dd0f01f343d8081d3de4c94a355815b2ad58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 13:30:21 +0100 Subject: [PATCH 040/223] Remove SystemService#active --- library/system/src/lib/yast2/system_service.rb | 4 +--- library/system/test/yast2/system_service_test.rb | 14 +++++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 209e6d4ac..8dfee7176 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -139,13 +139,11 @@ def active=(value) # Determine whether the service will be active after calling #save # # @return [Boolean] true if the service must be active; false otherwise - def active + def active? return new_value_for(:active) if changed_value?(:active) service.active? end - alias_method :active?, :active - # Saves changes to the underlying system # # @param set_status [Boolean] Do not change service status. Useful when running on 1st stage. diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 5d4b479a8..134af713d 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -167,7 +167,7 @@ let(:active?) { false } it "sets #active to true" do - expect { system_service.active = true }.to change { system_service.active } + expect { system_service.active = true }.to change { system_service.active? } .from(false).to(true) end @@ -187,7 +187,7 @@ let(:active?) { true } it "sets #active to false" do - expect { system_service.active = false }.to change { system_service.active } + expect { system_service.active = false }.to change { system_service.active? } .from(true).to(false) end @@ -202,12 +202,12 @@ end end - describe "#active" do + describe "#active?" do context "when service is active" do let(:active?) { true } it "returns true" do - expect(system_service.active).to eq(true) + expect(system_service.active?).to eq(true) end end @@ -215,7 +215,7 @@ let(:active?) { false } it "returns false" do - expect(system_service.active).to eq(false) + expect(system_service.active?).to eq(false) end end @@ -225,7 +225,7 @@ end it "returns the given value" do - expect(system_service.active).to eq(false) + expect(system_service.active?).to eq(false) end end end @@ -374,7 +374,7 @@ context "when some value has been changed" do before do - system_service.active = !system_service.active + system_service.active = !system_service.active? end it "returns true" do From 6fcf56c74bcb1cba3474f5d8cb4078b64b0c932f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 16:34:47 +0100 Subject: [PATCH 041/223] Add SystemService#{start,stop} --- .../system/src/lib/yast2/system_service.rb | 42 ++++-- .../system/test/yast2/system_service_test.rb | 130 +++++++++++++----- 2 files changed, 123 insertions(+), 49 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 8dfee7176..6aaa4a383 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -52,7 +52,7 @@ class SystemService # @return [Yast::SystemdService] attr_reader :service - def_delegators :@service, :running?, :start, :stop, :restart, :name, :description + def_delegators :@service, :running?, :name, :description # @!method state # @@ -123,19 +123,6 @@ def start_mode=(mode) end end - # Sets whether the service should be active or not - # - # The given value will be applied after calling #save. - # - # @param value [Boolean] true to set this service as active - def active=(value) - if value == active? - unregister_change(:active) - else - register_change(:active, value) - end - end - # Determine whether the service will be active after calling #save # # @return [Boolean] true if the service must be active; false otherwise @@ -144,6 +131,20 @@ def active? service.active? end + # Sets the service to be started after calling #save + # + # @see #active= + def start + self.active = true + end + + # Sets the service to be stopped after calling #save + # + # @see #active= + def stop + self.active = false + end + # Saves changes to the underlying system # # @param set_status [Boolean] Do not change service status. Useful when running on 1st stage. @@ -204,6 +205,19 @@ def changed_value?(key) # @return [Hash] attr_reader :changes + # Sets whether the service should be active or not + # + # The given value will be applied after calling #save. + # + # @param value [Boolean] true to set this service as active + def active=(value) + if value == service.active? + unregister_change(:active) + else + register_change(:active, value) + end + end + # Get the current start_mode def current_start_mode @current_start_mode ||= diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 134af713d..4984cae6f 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -162,48 +162,98 @@ end end - describe "#active=" do - context "when set to true" do - let(:active?) { false } + describe "#start" do + let(:active?) { false } + + it "sets the service to be active" do + expect { system_service.start }.to change { system_service.active? } + .from(false).to(true) + end + + it "sets the service as changed" do + expect { system_service.start }.to change { system_service.changed_value?(:active) } + .from(false).to(true) + end + + context "and the service is already started (active)" do + let(:active?) { true } + + it "sets the service to stay as active" do + expect { system_service.start }.to_not change { system_service.active? } + end + + it "ignores the change" do + system_service.start + expect(system_service.changed?).to eq(false) + end + end - it "sets #active to true" do - expect { system_service.active = true }.to change { system_service.active? } + context "and the services was set to be stopped" do + let(:active?) { true } + + before do + system_service.stop + end + + it "sets the service to stay as active" do + expect { system_service.start }.to change { system_service.active? } .from(false).to(true) end - context "and the service is already active" do - before do - system_service.active = true - end + it "cancels the previous change" do + system_service.start + expect(system_service.changed?).to eq(false) + end + end + end - it "ignores the change" do - system_service.active = true - expect(system_service.changed?).to eq(false) - end + describe "#stop" do + let(:active?) { true } + + it "sets the service to be deactivated" do + expect { system_service.stop }.to change { system_service.active? } + .from(true).to(false) + end + + it "sets the service as changed" do + expect { system_service.stop }.to change { system_service.changed_value?(:active) } + .from(false).to(true) + end + + context "and the service is already stopped (inactive)" do + let(:active?) { false } + + it "sets the service to stay as inactive" do + expect { system_service.stop }.to_not change { system_service.active? } + end + + it "ignores the change" do + system_service.stop + expect(system_service.changed?).to eq(false) end end - context "when set to false" do - let(:active?) { true } + context "and the services was set to be stopped" do + let(:active?) { false } - it "sets #active to false" do - expect { system_service.active = false }.to change { system_service.active? } - .from(true).to(false) + before do + system_service.start end - context "and the service is already inactive" do - let(:active?) { false } + it "sets the service to stay as active" do + expect { system_service.stop }.to change { system_service.active? } + .from(true).to(false) + end - it "ignores the change" do - system_service.active = false - expect(system_service.changed?).to eq(false) - end + it "cancels the previous change" do + system_service.stop + expect(system_service.changed?).to eq(false) end end end describe "#active?" do - context "when service is active" do + context "when the underlying service is active" do let(:active?) { true } it "returns true" do @@ -211,7 +261,7 @@ end end - context "when service is inactive" do + context "when the underlying service is inactive" do let(:active?) { false } it "returns false" do @@ -219,12 +269,22 @@ end end - context "when an active value was given" do + context "when service was set to be started" do before do - system_service.active = false + system_service.start end - it "returns the given value" do + it "returns true" do + expect(system_service.active?).to eq(true) + end + end + + context "when service was set to be stopped" do + before do + system_service.stop + end + + it "returns true" do expect(system_service.active?).to eq(false) end end @@ -270,8 +330,8 @@ end end - context "when active is set to true" do - before { system_service.active = true } + context "when the service is set to be started" do + before { system_service.start } context "and the service is already active" do let(:active?) { true } @@ -297,8 +357,8 @@ end end - context "when active is set to false" do - before { system_service.active = false } + context "when the service is set to be stopped" do + before { system_service.stop } context "and the service is active" do let(:active?) { true } @@ -328,7 +388,7 @@ describe "#changed?" do context "when some change was made" do before do - system_service.active = false + system_service.stop end it "returns true" do @@ -374,7 +434,7 @@ context "when some value has been changed" do before do - system_service.active = !system_service.active? + system_service.stop end it "returns true" do From 2c0befee244b1f55da7185c95d1f2e4d311dcf92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 16:40:07 +0100 Subject: [PATCH 042/223] Add SystemService#toggle --- .../system/src/lib/yast2/system_service.rb | 7 ++++++ .../system/test/yast2/system_service_test.rb | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 6aaa4a383..0241d280e 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -145,6 +145,13 @@ def stop self.active = false end + # Toggles the service status + # + # @see #active= + def toggle + self.active = !active? + end + # Saves changes to the underlying system # # @param set_status [Boolean] Do not change service status. Useful when running on 1st stage. diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 4984cae6f..6460a1f76 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -252,6 +252,30 @@ end end + describe "#toggle" do + context "when the service is set to be started" do + before do + system_service.start + end + + it "sets the services to be stopped" do + expect { system_service.toggle }.to change { system_service.active? } + .from(true).to(false) + end + end + + context "when the service is set to be stopped" do + before do + system_service.stop + end + + it "sets the services to be started" do + expect { system_service.toggle }.to change { system_service.active? } + .from(false).to(true) + end + end + end + describe "#active?" do context "when the underlying service is active" do let(:active?) { true } From e1c0427284d2247f7a869f88a316c6335f4f37ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 6 Jul 2018 16:47:30 +0100 Subject: [PATCH 043/223] Update SystemService examples --- library/system/src/lib/yast2/system_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 0241d280e..486b453f7 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -39,12 +39,12 @@ module Yast2 # # @example Activating a service # cups = SystemService.find("cups") - # cups.active = true + # cups.start # cups.save # # @example Ignoring status changes on 1st stage # cups = SystemService.find("cups") - # cups.active = true + # cups.start # cups.save(ignore_status: true) class SystemService extend Forwardable From c562234032715d01ae44fdc8944b39c0ed3853a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 9 Jul 2018 12:50:15 +0100 Subject: [PATCH 044/223] Update from code review --- library/system/test/yast2/system_service_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 6460a1f76..fd44c861f 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -188,7 +188,7 @@ end end - context "and the services was set to be stopped" do + context "and the service was set to be stopped" do let(:active?) { true } before do @@ -308,7 +308,7 @@ system_service.stop end - it "returns true" do + it "returns false" do expect(system_service.active?).to eq(false) end end From 3b4418ce67867df9dd1cc17ff47616298a53ec10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 5 Jul 2018 15:38:18 +0100 Subject: [PATCH 045/223] Add error handling to SystemService#errors --- .../system/src/lib/yast2/system_service.rb | 69 ++++++++------- .../system/test/yast2/system_service_test.rb | 83 +++++++++++++++---- 2 files changed, 110 insertions(+), 42 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 486b453f7..d3b866014 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -49,20 +49,15 @@ module Yast2 class SystemService extend Forwardable - # @return [Yast::SystemdService] + # @return [Yast::SystemdServiceClass::Service] attr_reader :service - def_delegators :@service, :running?, :name, :description + # @return [Hash] Errors when trying to write changes to the + # underlying system. + attr_reader :errors - # @!method state - # - # @return [String] - def_delegator :@service, :active_state, :state - - # @!method substate - # - # @return [String] - def_delegator :@service, :sub_state, :substate + def_delegators :@service, :running?, :start, :stop, :restart, :active?, + :active_state, :sub_state, :name, :description class << self # Find a service @@ -89,6 +84,7 @@ def find_many(names) def initialize(service) @service = service @changes = {} + @errors = {} end # Returns the start mode @@ -156,6 +152,7 @@ def toggle # # @param set_status [Boolean] Do not change service status. Useful when running on 1st stage. def save(ignore_status: false) + clear_errors save_start_mode set_current_status unless ignore_status reload @@ -239,27 +236,43 @@ def current_start_mode # Sets start mode to the underlying system def save_start_mode - return unless changed_value?(:start_mode) - case new_value_for(:start_mode) - when :on_boot - service.enable - socket.disable - when :on_demand - service.disable - socket.enable - when :manual - service.disable - socket.disable - end + return unless changes[:start_mode] + result = + case changes[:start_mode] + when :on_boot + service.enable && socket.disable + when :on_demand + service.disable && socket.enable + when :manual + service.disable && socket.disable + end + register_error(:start_mode) unless result end # Sets service status def set_current_status - if new_value_for(:active) && !service.active? - service.start - elsif new_value_for(:active) == false && service.active? - service.stop - end + return if changes[:active].nil? + result = + if changes[:active] && !service.active? + service.start + elsif changes[:active] == false && service.active? + service.stop + end + register_error(:active) if result == false + end + + # Registers error information + # + # Stores the source of error and the value which caused it. + # + # @param key [Symbol] Source of error + def register_error(key) + errors[key] = changes[key] + end + + # Clears registered errors + def clear_errors + @errors.clear end # Returns the associated socket diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index fd44c861f..3671d2154 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -328,8 +328,8 @@ let(:start_mode) { :on_boot } it "enables the service to start on boot" do - expect(service).to receive(:enable) - expect(socket).to receive(:disable) + expect(service).to receive(:enable).and_return(true) + expect(socket).to receive(:disable).and_return(true) system_service.save end end @@ -338,8 +338,8 @@ let(:start_mode) { :on_demand } it "enables the socket" do - expect(service).to receive(:disable) - expect(socket).to receive(:enable) + expect(service).to receive(:disable).and_return(true) + expect(socket).to receive(:enable).and_return(true) system_service.save end end @@ -348,12 +348,25 @@ let(:start_mode) { :manual } it "disables the service and the socket" do - expect(service).to receive(:disable) - expect(socket).to receive(:disable) + expect(service).to receive(:disable).and_return(true) + expect(socket).to receive(:disable).and_return(true) system_service.save end end + context "when setting the start_mode fails" do + let(:start_mode) { :on_demand } + + before do + allow(service).to receive(:disable).and_return(false) + end + + it "registers the error" do + system_service.save + expect(system_service.errors).to eq(start_mode: :on_demand) + end + end + context "when the service is set to be started" do before { system_service.start } @@ -370,13 +383,34 @@ let(:active?) { false } it "tries to activate the service" do - expect(service).to receive(:start) + expect(service).to receive(:start).and_return(true) system_service.save end - it "does not active the service if the status must be ignored" do - expect(service).to_not receive(:start) - system_service.save(ignore_status: true) + context "and the service is successfully started" do + it "does not register any error" do + allow(service).to receive(:start).and_return(true) + system_service.save + expect(system_service.errors).to_not have_key(:activate) + end + end + + context "and the service could not be started" do + before do + allow(service).to receive(:start).and_return(false) + end + + it "registers the error" do + system_service.save + expect(system_service.errors).to eq(active: true) + end + end + + context "and the status must be ignored" do + it "does not try to activate the service" do + expect(service).to_not receive(:start) + system_service.save(ignore_status: true) + end end end end @@ -392,13 +426,34 @@ system_service.save end - it "does not stop the service if the status must be ignored" do - expect(service).to_not receive(:start) - system_service.save(ignore_status: true) + context "and the service is successfully stopped" do + it "does not register any error" do + allow(service).to receive(:stop).and_return(true) + system_service.save + expect(system_service.errors).to_not have_key(:activate) + end + end + + context "and the service could not be stopped" do + before do + allow(service).to receive(:stop).and_return(false) + end + + it "registers the error" do + system_service.save + expect(system_service.errors).to eq(active: false) + end + end + + context "and the status must be ignored" do + it "does not try to stop the service" do + expect(service).to_not receive(:start) + system_service.save(ignore_status: true) + end end end - context "and the service is inactive" do + context "and the service is already inactive" do let(:active?) { false } it "does not try to stop the service again" do From 47d657d57860b9a7ee2e463b52474581582fe091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 16 Jul 2018 14:45:53 +0100 Subject: [PATCH 046/223] Add SystemdUnit#static? --- library/system/src/lib/yast2/system_service.rb | 2 +- library/systemd/src/lib/yast2/systemd_unit.rb | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index d3b866014..6a8756049 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -57,7 +57,7 @@ class SystemService attr_reader :errors def_delegators :@service, :running?, :start, :stop, :restart, :active?, - :active_state, :sub_state, :name, :description + :active_state, :sub_state, :name, :description, :static? class << self # Find a service diff --git a/library/systemd/src/lib/yast2/systemd_unit.rb b/library/systemd/src/lib/yast2/systemd_unit.rb index 4fa1397d0..ddd3fe7ff 100644 --- a/library/systemd/src/lib/yast2/systemd_unit.rb +++ b/library/systemd/src/lib/yast2/systemd_unit.rb @@ -72,7 +72,6 @@ class PropMap < Hash # with ruby 2.4 delegating ostruct with Forwardable start to write warning # so define it manually (bsc#1049433) -<<<<<<< HEAD FORWARDED_METHODS = [ :id, :path, @@ -88,11 +87,6 @@ class PropMap < Hash private_constant :FORWARDED_METHODS FORWARDED_METHODS.each { |m| define_method(m) { properties.public_send(m) } } -======= - [:id, :path, :description, :active?, :enabled?, :loaded?, :active_state, :sub_state].each do |m| - define_method(m) { properties.public_send(m) } - end ->>>>>>> Add methods to read the service state # @return [String] eg. "apache2" # (the canonical one, may be different from unit_name) @@ -258,6 +252,7 @@ def initialize(systemd_unit, property_text) end extract_properties +<<<<<<< HEAD self[:active?] = ACTIVE_STATES.include?(active_state) self[:running?] = sub_state == "running" self[:loaded?] = load_state == "loaded" @@ -266,6 +261,15 @@ def initialize(systemd_unit, property_text) self[:enabled?] = read_enabled_state self[:supported?] = SUPPORTED_STATES.include?(unit_file_state) self[:can_reload?] = can_reload == "yes" +======= + self[:active?] = ACTIVE_STATES.include?(active_state) + self[:running?] = sub_state == "running" + self[:loaded?] = load_state == "loaded" + self[:not_found?] = load_state == "not-found" + self[:static?] = load_state == "static" + self[:enabled?] = read_enabled_state + self[:supported?] = SUPPORTED_STATES.include?(unit_file_state) +>>>>>>> Add SystemdUnit#static? end private From 37f8d48ce17f10fb3ed319d09c008b6c1513e3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 17 Jul 2018 15:46:44 +0100 Subject: [PATCH 047/223] Update from code review --- library/system/src/lib/yast2/system_service.rb | 10 ++++++++++ library/system/test/yast2/system_service_test.rb | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 6a8756049..47c79c069 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -56,6 +56,16 @@ class SystemService # underlying system. attr_reader :errors + # @!method state + # + # @return [String] + def_delegator :@service, :active_state, :state + + # @!method substate + # + # @return [String] + def_delegator :@service, :sub_state, :substate + def_delegators :@service, :running?, :start, :stop, :restart, :active?, :active_state, :sub_state, :name, :description, :static? diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb index 3671d2154..f2cc3c9f6 100755 --- a/library/system/test/yast2/system_service_test.rb +++ b/library/system/test/yast2/system_service_test.rb @@ -391,7 +391,7 @@ it "does not register any error" do allow(service).to receive(:start).and_return(true) system_service.save - expect(system_service.errors).to_not have_key(:activate) + expect(system_service.errors).to_not have_key(:active) end end @@ -430,7 +430,7 @@ it "does not register any error" do allow(service).to receive(:stop).and_return(true) system_service.save - expect(system_service.errors).to_not have_key(:activate) + expect(system_service.errors).to_not have_key(:active) end end From 2bc409dfcf34de429e01e292e024ea439a92eeda Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 18 Jul 2018 08:23:54 +0200 Subject: [PATCH 048/223] try alternative naming --- .../systemd/src/lib/yast2/service_widget.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 2e6006c6e..d67975023 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -124,7 +124,7 @@ def status def action_widget ComboBox( Id(:service_widget_action), - "After writting settings:", + "After writting configuration:", action_items ) end @@ -135,18 +135,18 @@ def action_items reload_label = mixed ? _("Reload active and start inactive") : _("Reload") current_action = service_configuration.action [ - Item(Id(:service_widget_action_start), _("Ensure it is running"), current_action == :start), - Item(Id(:service_widget_action_stop), _("Ensure it is stopped"), current_action == :stop), + Item(Id(:service_widget_action_start), _("Activate"), current_action == :start), + Item(Id(:service_widget_action_stop), _("Inactivate"), current_action == :stop), Item(Id(:service_widget_action_restart), restart_label, current_action == :restart), Item(Id(:service_widget_action_reload), reload_label, current_action == :reload), - Item(Id(:service_widget_action_nothing), _("Nothing"), current_action == :nothing) + Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action == :nothing) ] end def autostart_widget ComboBox( Id(:service_widget_autostart), - "Start Service Automatically:", + "After reboot:", autostart_items ) end @@ -155,10 +155,10 @@ def autostart_items current_autostart = service_configuration.autostart keep = [:inconsistent, :unknown].include?(current_autostart) [ - Item(Id(:service_widget_autostart_on_boot), _("During boot"), current_autostart == :on_boot), - Item(Id(:service_widget_autostart_on_demand), _("On demand"), current_autostart == :on_demand), - Item(Id(:service_widget_autostart_manual), _("Never"), current_autostart == :manual), - Item(Id(:service_widget_autostart_inconsistent), _("Do not change"), keep) + Item(Id(:service_widget_autostart_on_boot), _("Start"), current_autostart == :on_boot), + Item(Id(:service_widget_autostart_on_demand), _("Start On demand"), current_autostart == :on_demand), + Item(Id(:service_widget_autostart_manual), _("Do not start"), current_autostart == :manual), + Item(Id(:service_widget_autostart_inconsistent), _("Keep current settings"), keep) ] end end From 73e7ac0ea3958f385d54c1d7b95cfad8a4e7813e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 18 Jul 2018 09:53:32 +0100 Subject: [PATCH 049/223] Rename SystemService#reload to #reset --- library/system/src/lib/yast2/system_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb index 47c79c069..44517af67 100644 --- a/library/system/src/lib/yast2/system_service.rb +++ b/library/system/src/lib/yast2/system_service.rb @@ -165,11 +165,11 @@ def save(ignore_status: false) clear_errors save_start_mode set_current_status unless ignore_status - reload + reset end # Reverts stored changes - def reload + def reset clear_changes @current_start_mode = nil end From 23fa8a11bc647ddc7e0b876083b560d55d09d018 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 18 Jul 2018 15:48:32 +0200 Subject: [PATCH 050/223] improved naming --- library/systemd/src/lib/yast2/service_widget.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index d67975023..237c7aeeb 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -131,14 +131,12 @@ def action_widget def action_items mixed = [:inconsistent, :unknown].include?(service_configuration.status) - restart_label = mixed ? _("Restart active and start inactive") : _("Restart") - reload_label = mixed ? _("Reload active and start inactive") : _("Reload") current_action = service_configuration.action [ - Item(Id(:service_widget_action_start), _("Activate"), current_action == :start), - Item(Id(:service_widget_action_stop), _("Inactivate"), current_action == :stop), - Item(Id(:service_widget_action_restart), restart_label, current_action == :restart), - Item(Id(:service_widget_action_reload), reload_label, current_action == :reload), + Item(Id(:service_widget_action_start), _("Start"), current_action == :start), + Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop), + Item(Id(:service_widget_action_restart), _("Restart"), restart_label, current_action == :restart), + Item(Id(:service_widget_action_reload), _("Reload"), current_action == :reload), Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action == :nothing) ] end @@ -155,8 +153,8 @@ def autostart_items current_autostart = service_configuration.autostart keep = [:inconsistent, :unknown].include?(current_autostart) [ - Item(Id(:service_widget_autostart_on_boot), _("Start"), current_autostart == :on_boot), - Item(Id(:service_widget_autostart_on_demand), _("Start On demand"), current_autostart == :on_demand), + Item(Id(:service_widget_autostart_on_boot), _("Start on boot"), current_autostart == :on_boot), + Item(Id(:service_widget_autostart_on_demand), _("Start on demand"), current_autostart == :on_demand), Item(Id(:service_widget_autostart_manual), _("Do not start"), current_autostart == :manual), Item(Id(:service_widget_autostart_inconsistent), _("Keep current settings"), keep) ] From 09f68635234d55c7342a3d0eef4b6eb3da6ec0fe Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 18 Jul 2018 16:59:54 +0200 Subject: [PATCH 051/223] add filtering to widget --- .../src/lib/yast2/service_configuration.rb | 9 ++++++ .../systemd/src/lib/yast2/service_widget.rb | 30 +++++++++++-------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb index f5b240fa4..e8d22dd04 100644 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ b/library/systemd/src/lib/yast2/service_configuration.rb @@ -108,6 +108,14 @@ def autostart @autostart end + # Returns system autostart configuration + # @see {#autostart} + def system_autostart + read unless @system_autostart + + @system_autostart + end + AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent].freeze # sets autostart configuration. # @param [:on_boot, :on_demand, :manual, :inconsistent] autostart @@ -157,6 +165,7 @@ def read_autostart else :inconsistent end + @system_autostart = @autostart rescue Yast::SystemctlError => e log.error "systemctl failure: #{e.inspect}" @autostart = :unknown diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 237c7aeeb..761abd22d 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -132,13 +132,14 @@ def action_widget def action_items mixed = [:inconsistent, :unknown].include?(service_configuration.status) current_action = service_configuration.action - [ - Item(Id(:service_widget_action_start), _("Start"), current_action == :start), - Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop), - Item(Id(:service_widget_action_restart), _("Restart"), restart_label, current_action == :restart), - Item(Id(:service_widget_action_reload), _("Reload"), current_action == :reload), - Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action == :nothing) - ] + res = [] + res << Item(Id(:service_widget_action_start), _("Start"), current_action == :start) unless service_configuration.status == :active + res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) unless service_configuration.status == :inactive + res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) unless service_configuration.status == :inactive + res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service_configuration.status != :inactive && service_configuration.support_reload? + res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action == :nothing) + + res end def autostart_widget @@ -152,12 +153,15 @@ def autostart_widget def autostart_items current_autostart = service_configuration.autostart keep = [:inconsistent, :unknown].include?(current_autostart) - [ - Item(Id(:service_widget_autostart_on_boot), _("Start on boot"), current_autostart == :on_boot), - Item(Id(:service_widget_autostart_on_demand), _("Start on demand"), current_autostart == :on_demand), - Item(Id(:service_widget_autostart_manual), _("Do not start"), current_autostart == :manual), - Item(Id(:service_widget_autostart_inconsistent), _("Keep current settings"), keep) - ] + keep_option = [:inconsistent, :unknown].include?(service_configuration.system_autostart) + res = [] + + res << Item(Id(:service_widget_autostart_on_boot), _("Start on boot"), current_autostart == :on_boot) + res << Item(Id(:service_widget_autostart_on_demand), _("Start on demand"), current_autostart == :on_demand) if service_configuration.support_on_demand? + res << Item(Id(:service_widget_autostart_manual), _("Do not start"), current_autostart == :manual) + res << Item(Id(:service_widget_autostart_inconsistent), _("Keep current settings"), keep) if keep_option + + res end end end From 07de1e839aa635f1c68aca3a63351d1f7c9ab2e2 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 19 Jul 2018 15:33:36 +0200 Subject: [PATCH 052/223] add compount service --- .../systemd/src/lib/yast2/compound_service.rb | 202 +++++++++++++++++ .../src/lib/yast2/service_configuration.rb | 208 ------------------ 2 files changed, 202 insertions(+), 208 deletions(-) create mode 100644 library/systemd/src/lib/yast2/compound_service.rb delete mode 100644 library/systemd/src/lib/yast2/service_configuration.rb diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb new file mode 100644 index 000000000..7327c0ddb --- /dev/null +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -0,0 +1,202 @@ +require "yast" + +require "yast2/system_service" + +module Yast2 + # Class that represents several related services that need to be synchronized. + # It mimics behavior of single SystemService, just adds additional possible states. + class CompoungService + include Yast::Logger + + # managed services + # @return [Yast2::ServiceConfiguration] + attr_reader :services + + # creates new configuration that holds state for given services + # @param services services to configure + # @param reload if use reload instead of restart action. If + # service does not support reload or does not run, then restart is used. + # + # @example three services + # config = Yast2::ServiceConfiguration.new(s1, s2, s3) + def initialize(*services) + if services.any? { |s| !s.is_a?(Yast2::SystemdService) } + raise ArgumentError, "Services can be only System Service - #{services.inspect}" + end + + @services = services + end + + # writes services new status + # @raise when set service to target state failed + def save(ignore_status: false) + services.each { |s| s.save(ignore_status: ignore_status) } + reset # to be consistent with underlaying services + end + + # returns current running status. + # @return [true, false, :inconsistent] current status of + # services: + # + # - `true` when all services are active + # - `false` when all services are inactive + # - `:inconsistent` when part of services is active and part not. + def current_active? + return true if services.all?(&:current_active?) + return false if services.none?(&:current_active?) + :inconsistent + end + + # returns true if any service supports reload + def support_reload? + services.any?(&:support_reload?) + end + + def start_modes + @start_modes ||= services.map(&:start_modes).reduce(:+).uniq + end + + def search_term + services.map(&:search_term).reduce(:+).uniq + end + + # returns currently set target action. If it is not yet set it returns + # `:nothing`. + # @return [:start, :stop, :restart, :nothing] action for all services: + # + # - `:start` to start all services. If service is already active, do nothing. + # if services has socket it starts socket instead of service. + # - `:stop` to stop all services. If service already is inactive, do nothing. + # - `:restart` restart all services. If service is inactive, it is started. + # - `:reload` reload all services that support it and restart that does not + # support it. If service is inactive, it is started. + # - `nil` do not touch anything. + def action + # TODO: check for inconsistencies? + services.first.action + end + + ACTIONS = [:start, :stop, :reload, :restart, :nothing].freeze + # sets target action for services + # @param action[:start, :stop, :restart, :reload, :nothing] for possible values and + # its explanation please see return value of {target_action} + def action=(action) + if !ACTIONS.include?(action) + raise ArgumentError, "Invalid parameter #{action.inspect}" + end + + @action = action + end + + # returns current system start mode. + # @return [:on_boot, :on_demand, :manual, :inconsistent] start_mode configuration: + # + # - `:on_boot` all services start during boot. + # - `:on_demand` all sockets associated with services is enabled and + # for services that does not have socket, it starts on boot. + # - `:manual` all services and all its associated sockets is disabled. + # - `:inconsistent` mixture of states + # @see Yast::SystemService#current_start_mode + # @note additional state `:inconsistent` that is not in SystemService + def current_start_mode + @current_start_mode ||= services_mode(method(:has_current_mode)) + end + + # returns configuration for start mode. + # @return [:on_boot, :on_demand, :manual, :inconsistent] start_mode configuration: + # + # - `:on_boot` when start mode is set to on_boot for all services. + # - `:on_demand` when start mode is set to on_demand for services that support it and on_boot + # otherwise. + # - `:manual` when start mode is set to manual for all services. + # - `:inconsistent` when services have mixture of start modes + # @see Yast::SystemService#start_mode + # @note additional state `:inconsistent` that is not in SystemService + def start_mode + services_mode(method(:has_mode)) + end + + AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent].freeze + # sets start mode configuration. + # @param [:on_boot, :on_demand, :manual, :inconsistent] sets start_mode. + # See {Yast2::SystemService.start_mode=}. Additionally inconsistent is used + # to keep current start mode. For each service. + def start_mode=(configuration) + if !AUTOSTART_OPTIONS.include?(configuration) + raise ArgumentError, "Invalid parameter #{configuration.inspect}" + end + + if configuration == :inconsistent + reset(exclude: [:action]) + else + services.all { |s| s.start_mode = configuration } + end + end + + # returns true if any allow start on demand + def support_start_on_demand? + @services.any?(&:support_start_on_demand?) + end + + # resets changes. + # @param exclude [Array] exclude from reset some parts. Now supported: `:action` and + # `:start_mode`. + def reset(exclude: []) + old_action = action + old_start_mode = start_mode + services.all(&:reset) + @current_start_mode = nil + public_send(old_action) if old_action != nil && !exclude.include?(:action) + if old_start_mode != :inconsistent && !exclude.include?(:start_mode) + self.start_mode = old_start_mode + end + end + + def start + services.each(&:start) + end + + def stop + services.each(&:stop) + end + + def restart + services.each(&:restart) + end + + def reload + services.each(&:reload) + end + + private + + def has_current_mode(start_mode) + ->(service) { service.current_start_mode == start_mode } + end + + def has_mode(start_mode) + ->(service) { service.start_mode == start_mode } + end + + def services_mode(mode_method) + if services.all?(&mode_method.call(:enabled?)) + :on_boot + elsif services.all?(&mode_method.call(:manual)) + :manual + elsif services_with_socket.all?(&mode_method.call(:on_demand)) && + services_without_socket.all?(&mode_method.call(:on_boot)) + :on_demand + else + :inconsistent + end + end + + def services_with_socket + @services_with_socket ||= services.select{ |s| s.start_modes.include?(:on_demand) } + end + + def services_without_socket + @services_without_socket ||= services.reject{ |s| s.start_modes.include?(:on_demand) } + end + end +end diff --git a/library/systemd/src/lib/yast2/service_configuration.rb b/library/systemd/src/lib/yast2/service_configuration.rb deleted file mode 100644 index e8d22dd04..000000000 --- a/library/systemd/src/lib/yast2/service_configuration.rb +++ /dev/null @@ -1,208 +0,0 @@ -require "yast" - -Yast.import "SystemdService" - -module Yast2 - # Class that holds configuration for single or multiple services. It allows - # to read current status from system. It allows to modify target state. And it - # allows to write that target state so system reflects it. - # Its common usage is with {Yast2::ServiceWidget} which allows to modify - # configuration and write it when user approve whole dialog. - class ServiceConfiguration - include Yast::Logger - - # services managed by this configuration - attr_reader :services - - # creates new configuration that holds state for given services - # @param services services to configure - # @param reload if use reload instead of restart action. If - # service does not support reload or does not run, then restart is used. - # - # @example three services - # config = Yast2::ServiceConfiguration.new(s1, s2, s3) - def initialize(*services) - if services.any? { |s| !s.is_a?(Yast::SystemdServiceClass::Service) } - raise ArgumentError, "Services can be only Systemd Service - #{services.inspect}" - end - - @services = services - end - - # reads system status of services. Can be also used to reread current status. - # @raise when read services state failed - def read - # safe initial state when exception happen - @status = :unknown - @autostart = :unknown - - read_status - read_autostart - end - - # writes services new status - # @raise when set service to target state failed - def write - write_action - write_autostart - end - - # returns current running status, but when read failed return `:unknown`. - # @return [:active, :inactive, :inconsistent, :unknown] current status of - # services: - # - # - `:active` when all services are active - # - `:inactive` when all services are inactive - # - `:inconsistent` when part of services is active and part not. - # Can happen only when configuration handle multiple services - # - `:unknown` when read of current status failed. - def status - read unless @status - - @status - end - - # returns currently set target action. If it is not yet set it returns - # `:nothing`. - # @return [:start, :stop, :restart, :nothing] action for all services: - # - # - `:start` to start all services. If service is already active, do nothing. - # if services has socket it starts socket instead of service. - # - `:stop` to stop all services. If service already is inactive, do nothing. - # - `:restart` restart all services. If service is inactive, it is started. - # - `:reload` reload all services that support it and restart that does not - # support it. If service is inactive, it is started. - # - `:nothing` do not touch anything. - def action - return :nothing unless @action - - @action - end - - ACTIONS = [:start, :stop, :reload, :restart, :nothing].freeze - # sets target action for services - # @param action[:start, :stop, :restart, :reload, :nothing] for possible values and - # its explanation please see return value of {target_action} - def action=(action) - if !ACTIONS.include?(action) - raise ArgumentError, "Invalid parameter #{action.inspect}" - end - - @action = action - end - - # returns currently set autostart configuration. If it is not yet set it - # calls read and return current system state. - # @return [:on_boot, :on_demand, :manual, :inconsistent, :unknown] autostart - # configuration: - # - # - `:on_boot` all services start during boot. - # - `:on_demand` all sockets associated with services is enabled and - # for services that does not have socket, it starts on boot. - # - `:manual` all services and all its associated sockets is disabled. - # - `:inconsistent` mixture of states - # - `:unknown` when no state is set and read call failed. - def autostart - read unless @autostart - - @autostart - end - - # Returns system autostart configuration - # @see {#autostart} - def system_autostart - read unless @system_autostart - - @system_autostart - end - - AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent].freeze - # sets autostart configuration. - # @param [:on_boot, :on_demand, :manual, :inconsistent] autostart - # configuratio. For explanation please see {autostart} when - # `:inconsistent` means keep it as it is now. - def autostart=(configuration) - if !AUTOSTART_OPTIONS.include?(configuration) - raise ArgumentError, "Invalid parameter #{configuration.inspect}" - end - - @autostart = configuration - end - - # returns true if any service support reload - def support_reload? - # TODO: implement it - true - end - - # returns true if any service has socket start - def support_on_demand? - @services.any?(&:socket?) - end - - private - - def read_status - services_active = @services.map(&:active?) - - return @status = :active if services_active.all? - return @status = :inactive if services_active.none? - @status = :inconsistent - rescue Yast::SystemctlError => e - log.error "systemctl failure: #{e.inspect}" - @status = :unknown - end - - def read_autostart - sockets = @services.map(&:socket).compact - services_without_socket = @services.reject(&:socket) - @autostart = if sockets.all?(&:enabled?) && services_without_socket.all?(&:enabled?) - :on_demand - elsif @services.all?(&:enabled?) - :on_boot - elsif sockets.none?(&:enabled?) && @services.none?(&:enabled?) - :manual - else - :inconsistent - end - @system_autostart = @autostart - rescue Yast::SystemctlError => e - log.error "systemctl failure: #{e.inspect}" - @autostart = :unknown - end - - def write_autostart - case autostart - when :on_boot then @services.each { |s| s.start_mode = :boot } - when :on_demand - @services.each do |service| - service.start_mode = if service.start_modes.include?(:demand) - :demand - else - :boot - end - end - when :manual then @services.each { |s| s.start_mode = :manual } - when :inconsistent then log.info "keeping current autostart" - else - raise "Unexpected action #{autostart.inspect}" - end - end - - def write_action - case action - when :start - sockets = @services.map(&:socket).compact - services_without_socket = @services.reject(&:socket) - sockets.each(&:start) - services_without_socket.each(&:start) - when :stop then @services.each(&:stop) - when :reload then @services.each(&:reload_or_restart) - when :restart then @services.each(&:restart) - when :nothing then log.info "no action" - else - raise "Unexpected action #{action.inspect}" - end - end - end -end From 53a91180381704f4e409f3ef2334a2577b84f25e Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 20 Jul 2018 11:57:18 +0200 Subject: [PATCH 053/223] Changes from review --- .../systemd/src/lib/yast2/compound_service.rb | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 7327c0ddb..4db91aaa8 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -1,3 +1,24 @@ +# encoding: utf-8 + +# Copyright (c) [2018] 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 "yast" require "yast2/system_service" @@ -5,22 +26,20 @@ module Yast2 # Class that represents several related services that need to be synchronized. # It mimics behavior of single SystemService, just adds additional possible states. - class CompoungService + class CompoundService include Yast::Logger # managed services - # @return [Yast2::ServiceConfiguration] + # @return [Array] attr_reader :services # creates new configuration that holds state for given services - # @param services services to configure - # @param reload if use reload instead of restart action. If - # service does not support reload or does not run, then restart is used. + # @param services [Array] services to configure # # @example three services - # config = Yast2::ServiceConfiguration.new(s1, s2, s3) + # service = Yast2::CompountService.new(s1, s2, s3) def initialize(*services) - if services.any? { |s| !s.is_a?(Yast2::SystemdService) } + if services.any? { |s| !s.is_a?(Yast2::SystemService) } raise ArgumentError, "Services can be only System Service - #{services.inspect}" end @@ -29,9 +48,8 @@ def initialize(*services) # writes services new status # @raise when set service to target state failed - def save(ignore_status: false) - services.each { |s| s.save(ignore_status: ignore_status) } - reset # to be consistent with underlaying services + def save(keep_state: false) + services.each { |s| s.save(keep_state: keep_state) } end # returns current running status. @@ -76,18 +94,6 @@ def action services.first.action end - ACTIONS = [:start, :stop, :reload, :restart, :nothing].freeze - # sets target action for services - # @param action[:start, :stop, :restart, :reload, :nothing] for possible values and - # its explanation please see return value of {target_action} - def action=(action) - if !ACTIONS.include?(action) - raise ArgumentError, "Invalid parameter #{action.inspect}" - end - - @action = action - end - # returns current system start mode. # @return [:on_boot, :on_demand, :manual, :inconsistent] start_mode configuration: # @@ -179,7 +185,7 @@ def has_mode(start_mode) end def services_mode(mode_method) - if services.all?(&mode_method.call(:enabled?)) + if services.all?(&mode_method.call(:on_boot)) :on_boot elsif services.all?(&mode_method.call(:manual)) :manual @@ -192,11 +198,11 @@ def services_mode(mode_method) end def services_with_socket - @services_with_socket ||= services.select{ |s| s.start_modes.include?(:on_demand) } + @services_with_socket ||= services.select{ |s| s.support_start_on_demand? } end def services_without_socket - @services_without_socket ||= services.reject{ |s| s.start_modes.include?(:on_demand) } + @services_without_socket ||= services.reject{ |s| s.support_start_on_demand? } end end end From 538a9f06db7657cf2f901e8000d98fd8a6de99a3 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 20 Jul 2018 18:04:16 +0200 Subject: [PATCH 054/223] add initial tests for compound service --- .../systemd/src/lib/yast2/compound_service.rb | 4 +- .../test/yast2/compound_service_test.rb | 122 ++++++++++++++++++ .../test/yast2/service_configuration_test.rb | 106 --------------- 3 files changed, 124 insertions(+), 108 deletions(-) create mode 100755 library/systemd/test/yast2/compound_service_test.rb delete mode 100755 library/systemd/test/yast2/service_configuration_test.rb diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 4db91aaa8..4e5f27711 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -74,8 +74,8 @@ def start_modes @start_modes ||= services.map(&:start_modes).reduce(:+).uniq end - def search_term - services.map(&:search_term).reduce(:+).uniq + def keywords + services.map(&:keywords).reduce(:+).uniq end # returns currently set target action. If it is not yet set it returns diff --git a/library/systemd/test/yast2/compound_service_test.rb b/library/systemd/test/yast2/compound_service_test.rb new file mode 100755 index 000000000..000cb5f6f --- /dev/null +++ b/library/systemd/test/yast2/compound_service_test.rb @@ -0,0 +1,122 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2018] 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_relative "../test_helper" + +require "yast2/compound_service" + +describe Yast2::CompoundService do + def service(*args) + args[0] ||= {} + args[0][:"is_a?"] = true + double("Yast2::SystemService", *args).as_null_object + end + + describe ".new" do + it "raises ArgumentError if non service is passed" do + expect { described_class.new(nil) }.to raise_error(ArgumentError) + end + end + + describe "#save" do + it "delegates save to all services it handles" do + service1 = service + service2 = service + expect(service1).to receive(:save) + expect(service2).to receive(:save) + + service = described_class.new(service1, service2) + service.save + end + end + + describe "#current_active?" do + context "all services are active" do + subject { described_class.new(service(current_active?: true), service(current_active?: true)) } + + it "returns true" do + expect(subject.current_active?).to eq true + end + end + + context "all services are inactive" do + subject { described_class.new(service(current_active?: false), service(current_active?: false)) } + + it "returns false" do + expect(subject.current_active?).to eq false + end + end + + context "some services are active and some inactive" do + subject { described_class.new(service(current_active?: false), service(current_active?: true)) } + + it "returns :inconsistent" do + expect(subject.current_active?).to eq :inconsistent + end + end + end + + describe "#support_reload?" do + subject { described_class.new(service(support_reload?: true), service(support_reload?: false)) } + + it "returns true if any service support reload" do + expect(subject.support_reload?).to eq true + end + end + + describe "#start_modes" do + subject do + described_class.new( + service(start_modes: [:on_boot, :manual]), + service(start_modes: [:on_boot, :on_demand, :manual]) + ) + end + + it "returns all start_modes that any of service has" do + expect(subject.start_modes).to contain_exactly :on_boot, :on_demand, :manual + end + end + + describe "#keywords" do + subject do + described_class.new( + service(keywords: ["unita.service"]), + service(keywords: ["unitb.service", "unitb.socket"]) + ) + end + + it "returns all keywords that any of service has" do + expect(subject.keywords).to contain_exactly "unita.service", "unitb.socket", "unitb.service" + end + end + + describe "#action" do + it "returns action specified on given services" do + subject = described_class.new( + service(action: :reboot), + service(action: :reboot) + ) + + expect(subject.action).to eq :reboot + end + end +end diff --git a/library/systemd/test/yast2/service_configuration_test.rb b/library/systemd/test/yast2/service_configuration_test.rb deleted file mode 100755 index bc4cfab9d..000000000 --- a/library/systemd/test/yast2/service_configuration_test.rb +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "../test_helper" - -require "yast2/service_configuration" - -describe Yast2::ServiceConfiguration do - describe ".new" do - it "raises ArgumentError if non service is passed" do - expect { described_class.new(nil) }.to raise_error(ArgumentError) - end - end - - describe "#read" do - context "reading status" do - it "sets status to :active if all passed services is active" do - service1 = double(active?: true, is_a?: true).as_null_object - service2 = double(active?: true, is_a?: true).as_null_object - service_configuration = described_class.new(service1, service2) - - service_configuration.read - expect(service_configuration.status).to eq :active - end - - it "sets status to :inactive if all passed services is not active" do - service1 = double(active?: false, is_a?: true).as_null_object - service2 = double(active?: false, is_a?: true).as_null_object - service_configuration = described_class.new(service1, service2) - - service_configuration.read - expect(service_configuration.status).to eq :inactive - end - - it "sets status to :inconsistent if only some services is active" do - service1 = double(active?: false, is_a?: true).as_null_object - service2 = double(active?: true, is_a?: true).as_null_object - service_configuration = described_class.new(service1, service2) - - service_configuration.read - expect(service_configuration.status).to eq :inconsistent - end - end - - context "reading auto start" do - # TODO: write after start using startmode - end - end - - describe "#write" do - context "writting action" do - # TODO: it should delegate this logic with sockets to systemservice - it "calls start if action is :start" do - service1 = double(is_a?: true).as_null_object - socket1 = double.as_null_object - service2 = double(is_a?: true, socket: socket1).as_null_object - service_configuration = described_class.new(service1, service2) - - service_configuration.action = :start - expect(service1).to receive(:start) - expect(service2).to_not receive(:start) - expect(socket1).to receive(:start) - service_configuration.write - end - - it "calls stop if action is :stop" do - service1 = double(is_a?: true).as_null_object - service_configuration = described_class.new(service1) - - service_configuration.action = :stop - expect(service1).to receive(:stop) - service_configuration.write - end - - it "calls restart if action is :restart" do - service1 = double(is_a?: true).as_null_object - service_configuration = described_class.new(service1) - - service_configuration.action = :restart - expect(service1).to receive(:restart) - service_configuration.write - end - - it "calls reload or restart if action is :reload" do - service1 = double(is_a?: true).as_null_object - service_configuration = described_class.new(service1) - - service_configuration.action = :reload - expect(service1).to receive(:reload_or_restart) - service_configuration.write - end - - it "does nothing if action is :nothing" do - service1 = double(is_a?: true, active?: false, socket: nil, enabled?: false, "start_mode=": nil) - service_configuration = described_class.new(service1) - - service_configuration.action = :nothing - service_configuration.write - end - end - - context "writting auto start" do - # TODO: write after start using start_mode in system service - end - - end -end From 328c46f3cf741466911c44efb2c6a2dd3c2c0350 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Mon, 23 Jul 2018 15:39:50 +0200 Subject: [PATCH 055/223] finished with tests --- .../systemd/src/lib/yast2/compound_service.rb | 11 +- .../test/yast2/compound_service_test.rb | 257 +++++++++++++++++- 2 files changed, 260 insertions(+), 8 deletions(-) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 4e5f27711..8a0a17de9 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -134,8 +134,11 @@ def start_mode=(configuration) if configuration == :inconsistent reset(exclude: [:action]) + elsif configuration == :on_demand + services_with_socket.each { |s| s.start_mode = :on_demand } + services_without_socket.each { |s| s.start_mode = :on_boot } else - services.all { |s| s.start_mode = configuration } + services.each { |s| s.start_mode = configuration } end end @@ -150,10 +153,10 @@ def support_start_on_demand? def reset(exclude: []) old_action = action old_start_mode = start_mode - services.all(&:reset) + services.each(&:reset) @current_start_mode = nil - public_send(old_action) if old_action != nil && !exclude.include?(:action) - if old_start_mode != :inconsistent && !exclude.include?(:start_mode) + public_send(old_action) if old_action != nil && exclude.include?(:action) + if old_start_mode != :inconsistent && exclude.include?(:start_mode) self.start_mode = old_start_mode end end diff --git a/library/systemd/test/yast2/compound_service_test.rb b/library/systemd/test/yast2/compound_service_test.rb index 000cb5f6f..cc5597d99 100755 --- a/library/systemd/test/yast2/compound_service_test.rb +++ b/library/systemd/test/yast2/compound_service_test.rb @@ -31,6 +31,18 @@ def service(*args) double("Yast2::SystemService", *args).as_null_object end + let(:service1) { service } + let(:service2) { service } + + subject do + described_class.new( + service1, + service2 + ) + end + + + describe ".new" do it "raises ArgumentError if non service is passed" do expect { described_class.new(nil) }.to raise_error(ArgumentError) @@ -39,13 +51,10 @@ def service(*args) describe "#save" do it "delegates save to all services it handles" do - service1 = service - service2 = service expect(service1).to receive(:save) expect(service2).to receive(:save) - service = described_class.new(service1, service2) - service.save + subject.save end end @@ -119,4 +128,244 @@ def service(*args) expect(subject.action).to eq :reboot end end + + describe "#current_start_mode" do + context "all services start on boot currently" do + subject do + described_class.new( + service(current_start_mode: :on_boot), + service(current_start_mode: :on_boot) + ) + end + + it "returns :on_boot" do + expect(subject.current_start_mode).to eq :on_boot + end + end + + context "all services don't start automatic" do + subject do + described_class.new( + service(current_start_mode: :manual), + service(current_start_mode: :manual) + ) + end + + it "returns :manual" do + expect(subject.current_start_mode).to eq :manual + end + end + + context "services which supports it start on demand and rest start on boot" do + subject do + described_class.new( + service(current_start_mode: :on_demand, support_start_on_demand?: true), + service(current_start_mode: :on_boot, support_start_on_demand?: false) + ) + end + + it "returns :on_demand" do + expect(subject.current_start_mode).to eq :on_demand + end + end + + context "mixture of automatic start configuration" do + subject do + described_class.new( + service(current_start_mode: :on_boot), + service(current_start_mode: :manual) + ) + end + + it "returns :inconsistent" do + expect(subject.current_start_mode).to eq :inconsistent + end + end + end + + describe "#start_mode" do + context "all services are set to start on boot" do + subject do + described_class.new( + service(start_mode: :on_boot), + service(start_mode: :on_boot) + ) + end + + it "returns :on_boot" do + expect(subject.start_mode).to eq :on_boot + end + end + + context "all services are set to not start automatic" do + subject do + described_class.new( + service(start_mode: :manual), + service(start_mode: :manual) + ) + end + + it "returns :manual" do + expect(subject.start_mode).to eq :manual + end + end + + context "services which supports it are started on demand and rest start on boot" do + subject do + described_class.new( + service(start_mode: :on_demand, support_start_on_demand?: true), + service(start_mode: :on_boot, support_start_on_demand?: false) + ) + end + + it "returns :on_demand" do + expect(subject.start_mode).to eq :on_demand + end + end + + context "mixture of automatic start configuration" do + subject do + described_class.new( + service(start_mode: :on_boot), + service(start_mode: :manual) + ) + end + + it "returns :inconsistent" do + expect(subject.start_mode).to eq :inconsistent + end + end + end + + describe "#start_mode=" do + context "parameter is :on_boot" do + it "sets all services to start on boot" do + expect(service1).to receive(:start_mode=).with(:on_boot) + expect(service2).to receive(:start_mode=).with(:on_boot) + + subject.start_mode = :on_boot + end + end + + context "parameter is :manual" do + it "sets all services to not start automatic" do + expect(service1).to receive(:start_mode=).with(:manual) + expect(service2).to receive(:start_mode=).with(:manual) + + subject.start_mode = :manual + end + end + + context "parameter is :on_demand" do + let (:service1) { service(support_start_on_demand?: true) } + let (:service2) { service(support_start_on_demand?: false) } + + it "sets services that support it start on demand and rest on boot" do + expect(service1).to receive(:start_mode=).with(:on_demand) + expect(service2).to receive(:start_mode=).with(:on_boot) + + subject.start_mode = :on_demand + end + end + + + context "parameter is :inconsistent" do + it "resets automatic start configuration on all services" do + expect(subject).to receive(:reset).with(exclude: [:action]) + + subject.start_mode = :inconsistent + end + end + end + + describe "#support_start_on_demand?" do + subject { described_class.new(service(support_start_on_demand?: true), service(support_start_on_demand?: false)) } + + it "returns true if any service supports starting on demand" do + expect(subject.support_start_on_demand?).to eq true + end + end + + describe "#reset" do + it "calls reset on all services" do + expect(service1).to receive(:reset) + expect(service2).to receive(:reset) + + subject.reset + end + + context "action is excluded" do + let(:service1) { service(action: :start) } + it "redoes previous action on service" do + expect(service1).to receive(:start) + + subject.reset(exclude: [:action]) + end + end + + context "action is not excluded" do + let(:service1) { service(action: :start) } + it "does not redo previous action on service" do + expect(service1).to_not receive(:start) + + subject.reset(exclude: []) + end + end + + context "start_mode is excluded" do + let(:service1) { service(start_mode: :on_boot) } + it "set again start mode" do + expect(subject).to receive(:start_mode).and_return(:on_boot) + expect(service1).to receive(:start_mode=).with(:on_boot) + + subject.reset(exclude: [:start_mode]) + end + end + + context "start_mode is not excluded" do + let(:service1) { service(start_mode: :on_boot) } + it "does not set start mode" do + expect(subject).to receive(:start_mode).and_return(:on_boot) + expect(service1).to_not receive(:start_mode=) + + subject.reset(exclude: []) + end + end + end + + describe "#start" do + it "calls start for each service" do + expect(service1).to receive(:start) + expect(service2).to receive(:start) + + subject.start + end + end + + describe "#stop" do + it "calls stop for each service" do + expect(service1).to receive(:stop) + expect(service2).to receive(:stop) + + subject.stop + end + end + + describe "#restart" do + it "calls restart for each service" do + expect(service1).to receive(:restart) + expect(service2).to receive(:restart) + + subject.restart + end + end + + describe "#reload" do + it "calls reload for each service" do + expect(service1).to receive(:reload) + expect(service2).to receive(:reload) + + subject.reload + end + end end From 668f1ea7a9868cd4f31949b85ae707b5ffece1da Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Mon, 23 Jul 2018 18:19:58 +0200 Subject: [PATCH 056/223] adapt service widget --- .../systemd/src/lib/yast2/service_widget.rb | 79 +++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 761abd22d..25a9cbc25 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -1,25 +1,22 @@ module Yast2 # Class that represents widget that allows configuration of services. - # It uses to hold configuration {Yast2::ServiceConfiguration}. + # It uses to hold configuration {Yast2::SystemService} or {Yast2::CompoundService}. + # It can work with both and for usage it really depends if it should configure single + # or multiple services # # @example usage of widget with workflow with read + propose + show_dialog + write # class Workflow # def initialize - # service = Yast::SystemdService.find!("my_service") - # @service_configuration = Yast2::ServiceConfiguration.new(service) - # end - # - # def read - # @service_configuration.read + # @service = Yast::SystemService.find("my_service") # end # # def propose - # service_configuration.action = :restart - # service_configuration.autostart = :on_demand + # @service.action = :restart + # @service.start_mode = :on_demand # end # # def show_dialog - # service_widget = ServiceWidget.new(@service_configuration) + # service_widget = ServiceWidget.new(@service) # content = VBox( # ..., # service_widget.content @@ -33,24 +30,24 @@ module Yast2 # end # # def write - # service_configuration.write + # @service.save # end # end class ServiceWidget include Yast::I18n include Yast::Logger include Yast::UIShortcuts - # creates new widget instance for given service configuration - # @param service_configuration [Yast2::ServiceConfiguration] configuration holder - def initialize(service_configuration) + + # creates new widget instance for given service + # @param service_configuration [Yast2::SystemService,Yast2::CompoundService] service + def initialize(service) textdomain "base" - @service_configuration = service_configuration + @service = service end # gets widget term # @return def content - # TODO: disabling invalid action and autostart or kick it out? Frame( _("Service Configuration"), VBox( @@ -80,44 +77,48 @@ def handle_input(event_id) # Stores current configuration. Should be called always even when going # back so configuration is persistent when going again forward. def store + service.reset # so we start from scratch store_action store_autostart end private - attr_reader :service_configuration + attr_reader :service def store_action action = Yast::UI.QueryWidget(Id(:service_widget_action), :CurrentItem) return unless action - service_configuration.action = action.to_s.sub(/^service_widget_action_/, "").to_sym + action = action.to_s.sub(/^service_widget_action_/, "").to_sym + return if action == :nothing + + service.public_send(action) end def store_autostart autostart = Yast::UI.QueryWidget(Id(:service_widget_autostart), :CurrentItem) return unless autostart - service_configuration.autostart = autostart.to_s.sub(/^service_widget_autostart_/, "").to_sym + autostart = autostart.to_s.sub(/^service_widget_autostart_/, "").to_sym + return if autostart == :inconsistent + + service.start_mode = autostart end def status - case service_configuration.status + case service.current_active? # TRANSLATORS: Status of service - when :active + when true _("Active") - when :inactive + when false # TRANSLATORS: Status of service _("Inactive") when :inconsistent # TRANSLATORS: Status of service _("Partly Active") - when :unknown - # TRANSLATORS: Status of service - _("Unknown") else - raise "Unknown status #{service_configuration.status.inspect}" + raise "Unknown status #{service.current_active?.inspect}" end end @@ -130,14 +131,13 @@ def action_widget end def action_items - mixed = [:inconsistent, :unknown].include?(service_configuration.status) - current_action = service_configuration.action + current_action = service.action res = [] - res << Item(Id(:service_widget_action_start), _("Start"), current_action == :start) unless service_configuration.status == :active - res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) unless service_configuration.status == :inactive - res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) unless service_configuration.status == :inactive - res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service_configuration.status != :inactive && service_configuration.support_reload? - res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action == :nothing) + res << Item(Id(:service_widget_action_start), _("Start"), current_action == :start) if service.current_active? != true + res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) if service.current_active? != false + res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) if service.current_active? != true + res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service.current_active? != true && service.support_reload? + res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action == nil) res end @@ -151,15 +151,14 @@ def autostart_widget end def autostart_items - current_autostart = service_configuration.autostart - keep = [:inconsistent, :unknown].include?(current_autostart) - keep_option = [:inconsistent, :unknown].include?(service_configuration.system_autostart) + current_start_mode = service.start_mode + system_start_mode = service.current_start_mode res = [] - res << Item(Id(:service_widget_autostart_on_boot), _("Start on boot"), current_autostart == :on_boot) - res << Item(Id(:service_widget_autostart_on_demand), _("Start on demand"), current_autostart == :on_demand) if service_configuration.support_on_demand? - res << Item(Id(:service_widget_autostart_manual), _("Do not start"), current_autostart == :manual) - res << Item(Id(:service_widget_autostart_inconsistent), _("Keep current settings"), keep) if keep_option + res << Item(Id(:service_widget_autostart_on_boot), _("Start on boot"), current_start_mode == :on_boot) + res << Item(Id(:service_widget_autostart_on_demand), _("Start on demand"), current_start_mode == :on_demand) if service.support_start_on_demand? + res << Item(Id(:service_widget_autostart_manual), _("Do not start"), current_start_mode == :manual) + res << Item(Id(:service_widget_autostart_inconsistent), _("Keep current settings"), current_start_mode == :inconsistent) if system_start_mode == :inconsistent res end From d2a1be5562b3064df466e984231f7838e70e5180 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 24 Jul 2018 17:57:41 +0200 Subject: [PATCH 057/223] adapt example --- library/systemd/examples/service_widget.rb | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/library/systemd/examples/service_widget.rb b/library/systemd/examples/service_widget.rb index c87c759ac..18caee1c4 100644 --- a/library/systemd/examples/service_widget.rb +++ b/library/systemd/examples/service_widget.rb @@ -1,24 +1,19 @@ require "yast" require "yast2/service_widget" -require "yast2/service_configuration" - -Yast.import "SystemdService" +require "yast2/system_service" +require "yast2/compound_service" def service - @service ||= Yast::SystemdService.find!("cups.service") -end - -def service_configuration - @service_configuration ||= Yast2::ServiceConfiguration.new(service) + return @service if @service +# @service ||= Yast2::SystemService.find("cups.service") + service1 = Yast2::SystemService.find("cups.service") + service2 = Yast2::SystemService.find("dbus.service") + @service = Yast2::CompoundService.new(service1, service2) end def service_widget - @service_widget ||= Yast2::ServiceWidget.new(service_configuration) -end - -def read - service_configuration.read + @service_widget ||= Yast2::ServiceWidget.new(service) end include Yast::UIShortcuts @@ -39,7 +34,6 @@ def write true end -read ui_loop write From 7edf7415bfbf45b43c6f0f852f7e03ba2521e4cf Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 24 Jul 2018 18:50:27 +0200 Subject: [PATCH 058/223] make rubocop happy --- library/systemd/examples/service_widget.rb | 4 ++-- .../systemd/src/lib/yast2/compound_service.rb | 18 ++++++++---------- .../systemd/src/lib/yast2/service_widget.rb | 2 +- .../test/yast2/compound_service_test.rb | 7 ++----- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/library/systemd/examples/service_widget.rb b/library/systemd/examples/service_widget.rb index 18caee1c4..a45dfec45 100644 --- a/library/systemd/examples/service_widget.rb +++ b/library/systemd/examples/service_widget.rb @@ -5,8 +5,8 @@ require "yast2/compound_service" def service - return @service if @service -# @service ||= Yast2::SystemService.find("cups.service") + return @service if @service + # @service ||= Yast2::SystemService.find("cups.service") service1 = Yast2::SystemService.find("cups.service") service2 = Yast2::SystemService.find("dbus.service") @service = Yast2::CompoundService.new(service1, service2) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 8a0a17de9..982dcafbd 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -105,7 +105,7 @@ def action # @see Yast::SystemService#current_start_mode # @note additional state `:inconsistent` that is not in SystemService def current_start_mode - @current_start_mode ||= services_mode(method(:has_current_mode)) + @current_start_mode ||= services_mode(method(:current_mode?)) end # returns configuration for start mode. @@ -119,7 +119,7 @@ def current_start_mode # @see Yast::SystemService#start_mode # @note additional state `:inconsistent` that is not in SystemService def start_mode - services_mode(method(:has_mode)) + services_mode(method(:mode?)) end AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent].freeze @@ -155,10 +155,8 @@ def reset(exclude: []) old_start_mode = start_mode services.each(&:reset) @current_start_mode = nil - public_send(old_action) if old_action != nil && exclude.include?(:action) - if old_start_mode != :inconsistent && exclude.include?(:start_mode) - self.start_mode = old_start_mode - end + public_send(old_action) if !old_action.nil? && exclude.include?(:action) + self.start_mode = old_start_mode if old_start_mode != :inconsistent && exclude.include?(:start_mode) end def start @@ -179,11 +177,11 @@ def reload private - def has_current_mode(start_mode) + def current_mode?(start_mode) ->(service) { service.current_start_mode == start_mode } end - def has_mode(start_mode) + def mode?(start_mode) ->(service) { service.start_mode == start_mode } end @@ -201,11 +199,11 @@ def services_mode(mode_method) end def services_with_socket - @services_with_socket ||= services.select{ |s| s.support_start_on_demand? } + @services_with_socket ||= services.select(&:support_start_on_demand?) end def services_without_socket - @services_without_socket ||= services.reject{ |s| s.support_start_on_demand? } + @services_without_socket ||= services.reject(&:support_start_on_demand?) end end end diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 25a9cbc25..d5d07917a 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -137,7 +137,7 @@ def action_items res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) if service.current_active? != false res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) if service.current_active? != true res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service.current_active? != true && service.support_reload? - res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action == nil) + res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action.nil?) res end diff --git a/library/systemd/test/yast2/compound_service_test.rb b/library/systemd/test/yast2/compound_service_test.rb index cc5597d99..4019a1885 100755 --- a/library/systemd/test/yast2/compound_service_test.rb +++ b/library/systemd/test/yast2/compound_service_test.rb @@ -41,8 +41,6 @@ def service(*args) ) end - - describe ".new" do it "raises ArgumentError if non service is passed" do expect { described_class.new(nil) }.to raise_error(ArgumentError) @@ -257,8 +255,8 @@ def service(*args) end context "parameter is :on_demand" do - let (:service1) { service(support_start_on_demand?: true) } - let (:service2) { service(support_start_on_demand?: false) } + let(:service1) { service(support_start_on_demand?: true) } + let(:service2) { service(support_start_on_demand?: false) } it "sets services that support it start on demand and rest on boot" do expect(service1).to receive(:start_mode=).with(:on_demand) @@ -268,7 +266,6 @@ def service(*args) end end - context "parameter is :inconsistent" do it "resets automatic start configuration on all services" do expect(subject).to receive(:reset).with(exclude: [:action]) From 0fade728eb03829ad5aeff327c2185b2442006c3 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 12:41:18 +0200 Subject: [PATCH 059/223] add license --- .../systemd/src/lib/yast2/service_widget.rb | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index d5d07917a..81a1d5128 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -1,3 +1,24 @@ +# encoding: utf-8 + +# Copyright (c) [2018] 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. + module Yast2 # Class that represents widget that allows configuration of services. # It uses to hold configuration {Yast2::SystemService} or {Yast2::CompoundService}. From f28ca170ac31b656ad2316a0948372f1b03ae34b Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 12:52:48 +0200 Subject: [PATCH 060/223] adapt Makefile and add cwm version of service widget --- library/cwm/examples/service_widget.rb | 80 +++++++++++++++++++++++ library/cwm/src/Makefile.am | 1 + library/cwm/src/lib/cwm/service_widget.rb | 48 ++++++++++++++ library/systemd/src/Makefile.am | 3 +- 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 library/cwm/examples/service_widget.rb create mode 100644 library/cwm/src/lib/cwm/service_widget.rb diff --git a/library/cwm/examples/service_widget.rb b/library/cwm/examples/service_widget.rb new file mode 100644 index 000000000..823361f1b --- /dev/null +++ b/library/cwm/examples/service_widget.rb @@ -0,0 +1,80 @@ +# Simple example to demonstrate service widget API for CWM + +require_relative "example_helper" + +require "cwm" +require "cwm/service_widget" +require "yast2/system_service" + +Yast.import "CWM" +Yast.import "Wizard" +Yast.import "Popup" + +class LuckyNumberWidget < CWM::IntField + attr_reader :result, :minimum, :maximum + + def initialize + @minimum = 0 + @maximum = 1000 + end + + def label + _("Lucky number") + end + + def store + @result = value + end +end + +class GenerateButton < CWM::PushButton + def initialize(lucky_number_widget) + @lucky_number_widget = lucky_number_widget + end + + def label + _("Generate Lucky Number") + end + + def handle + Yast::Builtins.y2milestone("handle called") + @lucky_number_widget.value = rand(1000) + + nil + end +end + +module Yast + class ExampleDialog + include Yast::I18n + include Yast::UIShortcuts + def run + textdomain "example" + + lucky_number_widget = LuckyNumberWidget.new + button_widget = GenerateButton.new(lucky_number_widget) + service = ::Yast2::SystemService.find("cups.service") + puts service.public_methods + + contents = HBox( + button_widget, + lucky_number_widget, + ::CWM::ServiceWidget.new(service) + ) + + Yast::Wizard.CreateDialog + back_handler = proc { Yast::Popup.YesNo("Really go back?") } + abort_handler = proc { Yast::Popup.YesNo("Really abort?") } + CWM.show(contents, + caption: _("Lucky number"), + back_handler: back_handler, + abort_handler: abort_handler) + Yast::Wizard.CloseDialog + + lucky_number_widget.result + # service.save # do not call to avoid real system modification + end + end +end + +Yast::ExampleDialog.new.run diff --git a/library/cwm/src/Makefile.am b/library/cwm/src/Makefile.am index 93dfc0972..546528904 100644 --- a/library/cwm/src/Makefile.am +++ b/library/cwm/src/Makefile.am @@ -18,6 +18,7 @@ ycwm_DATA = \ lib/cwm/dialog.rb \ lib/cwm/page.rb \ lib/cwm/pager.rb \ + lib/cwm/service_widget.rb \ lib/cwm/replace_point.rb \ lib/cwm/rspec.rb \ lib/cwm/table.rb \ diff --git a/library/cwm/src/lib/cwm/service_widget.rb b/library/cwm/src/lib/cwm/service_widget.rb new file mode 100644 index 000000000..551a9da6a --- /dev/null +++ b/library/cwm/src/lib/cwm/service_widget.rb @@ -0,0 +1,48 @@ +# encoding: utf-8 + +# Copyright (c) [2018] 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 "yast2/service_widget" +require "cwm/custom_widget" + +module CWM + class ServiceWidget < CustomWidget + + # creates new widget instance for given service + # @param service_configuration [Yast2::SystemService,Yast2::CompoundService] service + def initialize(service) + @service_widget = Yast2::ServiceWidget.new(service) + self.handle_all_events = true + end + + def contents + @service_widget.content + end + + def handle(event) + @service_widget.handle_input(event["ID"]) + end + + def store + @service_widget.store + end + end +end + diff --git a/library/systemd/src/Makefile.am b/library/systemd/src/Makefile.am index af5cea22c..6ab3a9223 100644 --- a/library/systemd/src/Makefile.am +++ b/library/systemd/src/Makefile.am @@ -5,7 +5,8 @@ module_DATA = \ ylibdir = @ylibdir@/yast2 ylib_DATA = \ - lib/yast2/service_configuration.rb \ + lib/yast2/compound_service.rb \ + lib/yast2/service_widget.rb \ lib/yast2/systemctl.rb \ lib/yast2/systemd_unit.rb \ lib/yast2/system_service.rb From e9f47edc77c19751a588b4b1047712be9d312b4f Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 12:59:41 +0200 Subject: [PATCH 061/223] adapt Makefile --- library/systemd/test/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/test/Makefile.am b/library/systemd/test/Makefile.am index f523901b3..a720f48bb 100644 --- a/library/systemd/test/Makefile.am +++ b/library/systemd/test/Makefile.am @@ -1,10 +1,10 @@ TESTS = \ - yast2/service_configuration_test.rb \ systemctl_test.rb \ systemd_unit_test.rb \ systemd_socket_test.rb \ systemd_target_test.rb \ systemd_service_test.rb \ + yast2/compound_service_test.rb \ yast2/system_service_test.rb TEST_EXTENSIONS = .rb From 44b70d3852192501382dcb0a44098a95b2319aa6 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 13:03:26 +0200 Subject: [PATCH 062/223] fix obsolete file --- .../system/src/lib/yast2/system_service.rb | 324 ------------------ .../systemd/src/lib/yast2/service_widget.rb | 2 +- 2 files changed, 1 insertion(+), 325 deletions(-) delete mode 100644 library/system/src/lib/yast2/system_service.rb diff --git a/library/system/src/lib/yast2/system_service.rb b/library/system/src/lib/yast2/system_service.rb deleted file mode 100644 index 44517af67..000000000 --- a/library/system/src/lib/yast2/system_service.rb +++ /dev/null @@ -1,324 +0,0 @@ -# encoding: utf-8 - -# Copyright (c) [2018] 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 "forwardable" -Yast.import "SystemdService" - -module Yast2 - # This class represents a system service - # - # When talking about systemd, it might happen that a service is compose by a set of units - # (services, sockets, paths and so on). This class is able to group those units and - # offer an API to handle them together. - # - # Additionally, this class does not modify the underlying system until the #save - # method is called. - # - # @example Enabling a service - # cups = SystemService.find("cups") - # cups.start_mode = :boot - # cups.save - # - # @example Activating a service - # cups = SystemService.find("cups") - # cups.start - # cups.save - # - # @example Ignoring status changes on 1st stage - # cups = SystemService.find("cups") - # cups.start - # cups.save(ignore_status: true) - class SystemService - extend Forwardable - - # @return [Yast::SystemdServiceClass::Service] - attr_reader :service - - # @return [Hash] Errors when trying to write changes to the - # underlying system. - attr_reader :errors - - # @!method state - # - # @return [String] - def_delegator :@service, :active_state, :state - - # @!method substate - # - # @return [String] - def_delegator :@service, :sub_state, :substate - - def_delegators :@service, :running?, :start, :stop, :restart, :active?, - :active_state, :sub_state, :name, :description, :static? - - class << self - # Find a service - # - # @param name [String] Service name - # @return [SystemService,nil] System service or nil when not found - def find(name) - new(Yast::SystemdService.find(name)) - end - - # Finds service names - # - # This method finds a set of system services. Currently it is just a wrapper around - # SystemdService.find_many. - # - # @param names [Array] Service names to find - # @return [Array] Found system services - def find_many(names) - Yast::SystemdService.find_many(names).compact.map { |s| new(s) } - end - end - - # @param service [Yast::SystemdServiceClass::Service] - def initialize(service) - @service = service - @changes = {} - @errors = {} - end - - # Returns the start mode - # - # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, - # :manual and, in some cases, :on_demand). - # - # When the service (:on_boot) and the socket (:on_demand) are enabled, the start mode is translated - # to :on_boot. - # - # @return [Symbol] Start mode (:on_boot, :on_demand, :manual) - def start_mode - new_value_for(:start_mode) || current_start_mode - end - - # Sets the service start mode - # - # See {#start_modes} to find out the supported modes for a given service (usually :on_boot, - # :manual and, in some cases, :on_demand). The given value will be applied after calling #save. - # - # @see #start_modes - # @raise ArgumentError when mode is not valid - def start_mode=(mode) - if !start_modes.include?(mode) - raise ArgumentError, "Invalid start mode: '#{mode}' for service '#{service.name}'" - end - - if mode == current_start_mode - unregister_change(:start_mode) - else - register_change(:start_mode, mode) - end - end - - # Determine whether the service will be active after calling #save - # - # @return [Boolean] true if the service must be active; false otherwise - def active? - return new_value_for(:active) if changed_value?(:active) - service.active? - end - - # Sets the service to be started after calling #save - # - # @see #active= - def start - self.active = true - end - - # Sets the service to be stopped after calling #save - # - # @see #active= - def stop - self.active = false - end - - # Toggles the service status - # - # @see #active= - def toggle - self.active = !active? - end - - # Saves changes to the underlying system - # - # @param set_status [Boolean] Do not change service status. Useful when running on 1st stage. - def save(ignore_status: false) - clear_errors - save_start_mode - set_current_status unless ignore_status - reset - end - - # Reverts stored changes - def reset - clear_changes - @current_start_mode = nil - end - - # Determines whether the system has been changed or not - # - # @return [Boolean] true if the system has been changed - def changed? - !changes.empty? - end - - # Returns the list of supported start modes - # - # * :on_boot: The service will be started when the system boots. - # * :manual: The service is disabled and it will be started manually. - # * :on_demand: The service will be started on demand (using a Systemd socket). - # - # @return [Array] List of supported modes. - def start_modes - return @start_modes if @start_modes - @start_modes = [:on_boot, :manual] - @start_modes << :on_demand if socket - @start_modes - end - - # Terms to search for this service - # - # In case the service has an associated socket, the socket name - # is included as search term. - # - # @return [Array] e.g., #=> ["tftp.service", "tftp.socket"] - def search_terms - terms = [service.id] - terms << socket.id if socket - terms - end - - # Determines whether a value has been changed - # - # @return [Boolean] true if the value has been changed; false otherwise - def changed_value?(key) - changes.key?(key) - end - - private - - # @return [Hash] - attr_reader :changes - - # Sets whether the service should be active or not - # - # The given value will be applied after calling #save. - # - # @param value [Boolean] true to set this service as active - def active=(value) - if value == service.active? - unregister_change(:active) - else - register_change(:active, value) - end - end - - # Get the current start_mode - def current_start_mode - @current_start_mode ||= - if service.enabled? - :on_boot - elsif socket && socket.enabled? - :on_demand - else - :manual - end - end - - # Sets start mode to the underlying system - def save_start_mode - return unless changes[:start_mode] - result = - case changes[:start_mode] - when :on_boot - service.enable && socket.disable - when :on_demand - service.disable && socket.enable - when :manual - service.disable && socket.disable - end - register_error(:start_mode) unless result - end - - # Sets service status - def set_current_status - return if changes[:active].nil? - result = - if changes[:active] && !service.active? - service.start - elsif changes[:active] == false && service.active? - service.stop - end - register_error(:active) if result == false - end - - # Registers error information - # - # Stores the source of error and the value which caused it. - # - # @param key [Symbol] Source of error - def register_error(key) - errors[key] = changes[key] - end - - # Clears registered errors - def clear_errors - @errors.clear - end - - # Returns the associated socket - # - # @return [Yast::SystemdSocketClass::Socket] - def socket - service && service.socket - end - - # Unregisters change for a given key - # - # @param [Symbol] Change key - def unregister_change(key) - changes.delete(key) - end - - # Registers change for a given key - # - # @param [Symbol] Change key - # @param [Object] New value - def register_change(key, new_value) - changes[key] = new_value - end - - # Clears changes - def clear_changes - changes.clear - end - - # Returns the new value for a given key - # - # @param [Symbol] Change key - # @return [Object] New value - def new_value_for(key) - return nil unless changed_value?(key) - changes[key] - end - end -end diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 81a1d5128..23f200363 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -28,7 +28,7 @@ module Yast2 # @example usage of widget with workflow with read + propose + show_dialog + write # class Workflow # def initialize - # @service = Yast::SystemService.find("my_service") + # @service = Yast2::SystemService.find("my_service") # end # # def propose From 707ab5dce7ec06cc9cacd7b443ea87dfd52eb23f Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 13:08:19 +0200 Subject: [PATCH 063/223] avoid crash when event is nil --- library/cwm/src/lib/cwm/service_widget.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/cwm/src/lib/cwm/service_widget.rb b/library/cwm/src/lib/cwm/service_widget.rb index 551a9da6a..a22c5700a 100644 --- a/library/cwm/src/lib/cwm/service_widget.rb +++ b/library/cwm/src/lib/cwm/service_widget.rb @@ -37,6 +37,9 @@ def contents end def handle(event) + log.info "handling event #{event.inspect}" + return unless event + @service_widget.handle_input(event["ID"]) end From e1f29f6256cbacdcc66dff763e7fb7796c27631f Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 13:16:21 +0200 Subject: [PATCH 064/223] remove it also from Makefile --- library/system/src/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/library/system/src/Makefile.am b/library/system/src/Makefile.am index a11546f90..4a72f1e87 100644 --- a/library/system/src/Makefile.am +++ b/library/system/src/Makefile.am @@ -34,7 +34,6 @@ ylib_DATA = \ lib/yast2/fs_snapshot.rb \ lib/yast2/fs_snapshot_store.rb \ lib/yast2/target_file.rb \ - lib/yast2/system_service.rb \ lib/yast2/system_time.rb From 30ba239cce81773c129b4915a32c098ffccfecbb Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 15:07:00 +0200 Subject: [PATCH 065/223] add example of usage also in old cwm --- .../cwm/examples/service_widget_old_cwm.rb | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 library/cwm/examples/service_widget_old_cwm.rb diff --git a/library/cwm/examples/service_widget_old_cwm.rb b/library/cwm/examples/service_widget_old_cwm.rb new file mode 100644 index 000000000..103da9d68 --- /dev/null +++ b/library/cwm/examples/service_widget_old_cwm.rb @@ -0,0 +1,54 @@ +# Simple example to demonstrate service widget API for CWM + +require_relative "example_helper" + +require "cwm" +require "cwm/service_widget" +require "yast2/system_service" + +Yast.import "CWM" +Yast.import "Wizard" +Yast.import "Popup" + +module Yast + class ExampleDialog + include Yast::I18n + include Yast::UIShortcuts + def run + textdomain "example" + + service = ::Yast2::SystemService.find("cups.service") + service_widget = ::CWM::ServiceWidget.new(service) + widgets = { + "lucky_number_widget" => { + "widget" => :textentry, + "label" => _("Lucky Number") + }, + "button_widget" => { + "widget" => :push_button, + "label" => "Generate Lucky Number" + }, + service_widget.widget_id => service_widget.cwm_definition + } + + contents = HBox( + "button_widget", + "lucky_number_widget", + service_widget.widget_id + ) + + Yast::Wizard.CreateDialog + CWM.ShowAndRun( + "widget_names" => widgets.keys, + "widget_descr" => widgets, + "contents" => contents, + "caption" => _("Lucky number") + ) + Yast::Wizard.CloseDialog + + # service.save # do not call to avoid real system modification + end + end +end + +Yast::ExampleDialog.new.run From 0f2ff9ef7d72a72ffdcf0ba927203bb886662100 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 17:02:26 +0200 Subject: [PATCH 066/223] add tests for service widget --- .../systemd/src/lib/yast2/service_widget.rb | 4 + library/systemd/test/Makefile.am | 1 + .../systemd/test/yast2/service_widget_test.rb | 84 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100755 library/systemd/test/yast2/service_widget_test.rb diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 23f200363..81d14610b 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -19,6 +19,10 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. +require "yast" + +Yast.import "UI" + module Yast2 # Class that represents widget that allows configuration of services. # It uses to hold configuration {Yast2::SystemService} or {Yast2::CompoundService}. diff --git a/library/systemd/test/Makefile.am b/library/systemd/test/Makefile.am index a720f48bb..ea2bd6b8d 100644 --- a/library/systemd/test/Makefile.am +++ b/library/systemd/test/Makefile.am @@ -5,6 +5,7 @@ TESTS = \ systemd_target_test.rb \ systemd_service_test.rb \ yast2/compound_service_test.rb \ + yast2/service_widget_test.rb \ yast2/system_service_test.rb TEST_EXTENSIONS = .rb diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb new file mode 100755 index 000000000..f0516ded2 --- /dev/null +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2018] 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_relative "../test_helper" + +require "yast2/service_widget" + +describe Yast2::ServiceWidget do + let(:service) do + double( + "Yast2::SystemService", + current_active?: true, + start_mode: :on_boot, + current_start_mode: :on_boot + ).as_null_object + end + + subject do + described_class.new( + service + ) + end + + describe "#handle_input" do + it "returns nil" do + expect(subject.handle_input("")).to eq nil + end + end + + describe "#content" do + it "returns Term" do + expect(subject.content).to be_a(Yast::Term) + end + end + + describe "#store" do + before do + allow(Yast::UI).to receive(:QueryWidget) + end + + it "resets service configuration" do + expect(service).to receive(:reset) + + subject.store + end + + it "calls action according to widget" do + allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_action), :CurrentItem). + and_return(:service_widget_action_restart) + + expect(service).to receive(:restart) + + subject.store + end + + it "sets start_mode according to widget" do + allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_autostart), :CurrentItem). + and_return(:service_widget_autostart_manual) + + expect(service).to receive(:start_mode=).with(:manual) + + subject.store + end + end +end From 3c80c760799ca393ec8a99cdf69826cfbf9fa257 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 25 Jul 2018 17:19:40 +0200 Subject: [PATCH 067/223] make rubocop happy --- library/cwm/examples/service_widget_old_cwm.rb | 12 ++++++------ library/cwm/src/lib/cwm/service_widget.rb | 3 +-- library/systemd/test/yast2/service_widget_test.rb | 12 ++++++------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/library/cwm/examples/service_widget_old_cwm.rb b/library/cwm/examples/service_widget_old_cwm.rb index 103da9d68..48d4cf423 100644 --- a/library/cwm/examples/service_widget_old_cwm.rb +++ b/library/cwm/examples/service_widget_old_cwm.rb @@ -20,13 +20,13 @@ def run service = ::Yast2::SystemService.find("cups.service") service_widget = ::CWM::ServiceWidget.new(service) widgets = { - "lucky_number_widget" => { + "lucky_number_widget" => { "widget" => :textentry, - "label" => _("Lucky Number") + "label" => _("Lucky Number") }, - "button_widget" => { + "button_widget" => { "widget" => :push_button, - "label" => "Generate Lucky Number" + "label" => "Generate Lucky Number" }, service_widget.widget_id => service_widget.cwm_definition } @@ -41,8 +41,8 @@ def run CWM.ShowAndRun( "widget_names" => widgets.keys, "widget_descr" => widgets, - "contents" => contents, - "caption" => _("Lucky number") + "contents" => contents, + "caption" => _("Lucky number") ) Yast::Wizard.CloseDialog diff --git a/library/cwm/src/lib/cwm/service_widget.rb b/library/cwm/src/lib/cwm/service_widget.rb index a22c5700a..ae58561db 100644 --- a/library/cwm/src/lib/cwm/service_widget.rb +++ b/library/cwm/src/lib/cwm/service_widget.rb @@ -23,8 +23,8 @@ require "cwm/custom_widget" module CWM + # CWM wrapper for Yast2::ServiceWidget class ServiceWidget < CustomWidget - # creates new widget instance for given service # @param service_configuration [Yast2::SystemService,Yast2::CompoundService] service def initialize(service) @@ -48,4 +48,3 @@ def store end end end - diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb index f0516ded2..8b6e1b66c 100755 --- a/library/systemd/test/yast2/service_widget_test.rb +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -28,8 +28,8 @@ let(:service) do double( "Yast2::SystemService", - current_active?: true, - start_mode: :on_boot, + current_active?: true, + start_mode: :on_boot, current_start_mode: :on_boot ).as_null_object end @@ -64,8 +64,8 @@ end it "calls action according to widget" do - allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_action), :CurrentItem). - and_return(:service_widget_action_restart) + allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_action), :CurrentItem) + .and_return(:service_widget_action_restart) expect(service).to receive(:restart) @@ -73,8 +73,8 @@ end it "sets start_mode according to widget" do - allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_autostart), :CurrentItem). - and_return(:service_widget_autostart_manual) + allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_autostart), :CurrentItem) + .and_return(:service_widget_autostart_manual) expect(service).to receive(:start_mode=).with(:manual) From 04254f5f105b7f8226cf4b5b07c92b3c0e336142 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 26 Jul 2018 12:28:34 +0200 Subject: [PATCH 068/223] changes from review --- library/cwm/examples/service_widget.rb | 1 - library/cwm/src/lib/cwm/service_widget.rb | 2 +- .../systemd/src/lib/yast2/compound_service.rb | 19 +++++++++++++------ .../systemd/src/lib/yast2/service_widget.rb | 8 +++++--- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/library/cwm/examples/service_widget.rb b/library/cwm/examples/service_widget.rb index 823361f1b..6b6c68019 100644 --- a/library/cwm/examples/service_widget.rb +++ b/library/cwm/examples/service_widget.rb @@ -54,7 +54,6 @@ def run lucky_number_widget = LuckyNumberWidget.new button_widget = GenerateButton.new(lucky_number_widget) service = ::Yast2::SystemService.find("cups.service") - puts service.public_methods contents = HBox( button_widget, diff --git a/library/cwm/src/lib/cwm/service_widget.rb b/library/cwm/src/lib/cwm/service_widget.rb index ae58561db..468d135a2 100644 --- a/library/cwm/src/lib/cwm/service_widget.rb +++ b/library/cwm/src/lib/cwm/service_widget.rb @@ -26,7 +26,7 @@ module CWM # CWM wrapper for Yast2::ServiceWidget class ServiceWidget < CustomWidget # creates new widget instance for given service - # @param service_configuration [Yast2::SystemService,Yast2::CompoundService] service + # @param service [Yast2::SystemService,Yast2::CompoundService] service to be configured def initialize(service) @service_widget = Yast2::ServiceWidget.new(service) self.handle_all_events = true diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 982dcafbd..7a24cef8f 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -37,7 +37,7 @@ class CompoundService # @param services [Array] services to configure # # @example three services - # service = Yast2::CompountService.new(s1, s2, s3) + # service = Yast2::CompoundService.new(s1, s2, s3) def initialize(*services) if services.any? { |s| !s.is_a?(Yast2::SystemService) } raise ArgumentError, "Services can be only System Service - #{services.inspect}" @@ -70,17 +70,24 @@ def support_reload? services.any?(&:support_reload?) end + # Possible start modes taking into account all services. For example, + # if a service supports on boot and manually, but other service supports + # on demand too, the possible starts modes will the all of them: on boot, + # on demand and manually. + # + # @return [Array] start modes (:on_boot, :on_demand, :manual) def start_modes @start_modes ||= services.map(&:start_modes).reduce(:+).uniq end + # Keywords that can be used to search for involved underlaying units. def keywords services.map(&:keywords).reduce(:+).uniq end - # returns currently set target action. If it is not yet set it returns - # `:nothing`. - # @return [:start, :stop, :restart, :nothing] action for all services: + # returns currently set target action. If it is not set it returns + # `nil`. + # @return [:start, :stop, :restart, :reload, nil] action for all services: # # - `:start` to start all services. If service is already active, do nothing. # if services has socket it starts socket instead of service. @@ -100,7 +107,7 @@ def action # - `:on_boot` all services start during boot. # - `:on_demand` all sockets associated with services is enabled and # for services that does not have socket, it starts on boot. - # - `:manual` all services and all its associated sockets is disabled. + # - `:manual` all services and all its associated sockets are disabled. # - `:inconsistent` mixture of states # @see Yast::SystemService#current_start_mode # @note additional state `:inconsistent` that is not in SystemService @@ -126,7 +133,7 @@ def start_mode # sets start mode configuration. # @param [:on_boot, :on_demand, :manual, :inconsistent] sets start_mode. # See {Yast2::SystemService.start_mode=}. Additionally inconsistent is used - # to keep current start mode. For each service. + # to keep current start mode. def start_mode=(configuration) if !AUTOSTART_OPTIONS.include?(configuration) raise ArgumentError, "Invalid parameter #{configuration.inspect}" diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 81d14610b..12763a54b 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -64,7 +64,7 @@ class ServiceWidget include Yast::UIShortcuts # creates new widget instance for given service - # @param service_configuration [Yast2::SystemService,Yast2::CompoundService] service + # @param service [Yast2::SystemService,Yast2::CompoundService] service def initialize(service) textdomain "base" @service = service @@ -92,15 +92,17 @@ def content # handles event to dynamically react on user configuration. # For events that does not happen inside widget it is ignored. # @param event_id [Object] id of UI element that cause event - # @return [void] + # @return [nil] it returns nil as it should allow to continue dialog loop def handle_input(event_id) log.info "handle event #{event_id}" nil end - # Stores current configuration. Should be called always even when going + # Stores current configuration. Should be called always before dialog close, even when going # back so configuration is persistent when going again forward. + # @note it requires content of dialog to query, so cannot be called after DialogClose or if + # another dialog is displayed instead of the one with {#content} def store service.reset # so we start from scratch store_action From 80fe104344d1bd958490d817b4158b8e58df2feb Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 26 Jul 2018 14:31:22 +0200 Subject: [PATCH 069/223] adapt save and add errors method --- .../systemd/src/lib/yast2/compound_service.rb | 12 +++++++++++- .../systemd/test/yast2/compound_service_test.rb | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 7a24cef8f..4625bccd5 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -47,9 +47,11 @@ def initialize(*services) end # writes services new status - # @raise when set service to target state failed + # @return [true,false] returns false if saving any service failed def save(keep_state: false) services.each { |s| s.save(keep_state: keep_state) } + + errors.empty? end # returns current running status. @@ -154,6 +156,14 @@ def support_start_on_demand? @services.any?(&:support_start_on_demand?) end + # @return [Hash] Errors when trying to write changes to the + # underlying system. + def errors + @services.each_with_object({}) do |s, result| + result.merge!(s.errors) + end + end + # resets changes. # @param exclude [Array] exclude from reset some parts. Now supported: `:action` and # `:start_mode`. diff --git a/library/systemd/test/yast2/compound_service_test.rb b/library/systemd/test/yast2/compound_service_test.rb index 4019a1885..0890106fa 100755 --- a/library/systemd/test/yast2/compound_service_test.rb +++ b/library/systemd/test/yast2/compound_service_test.rb @@ -28,6 +28,7 @@ def service(*args) args[0] ||= {} args[0][:"is_a?"] = true + args[0][:errors] = {} double("Yast2::SystemService", *args).as_null_object end @@ -54,6 +55,21 @@ def service(*args) subject.save end + + it "returns false if any service save failed" do + allow(service2).to receive(:errors).and_return(start_mode: :on_boot) + + expect(subject.save).to eq false + end + end + + describe "#errors" do + it "returns merge of all underlaying services errors" do + allow(service1).to receive(:errors).and_return(action: :restart) + allow(service2).to receive(:errors).and_return(start_mode: :on_boot) + + expect(subject.errors).to eq action: :restart, start_mode: :on_boot + end end describe "#current_active?" do From 4adbf862e8733e766561afaa2d75bfa50739d396 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 26 Jul 2018 14:42:29 +0200 Subject: [PATCH 070/223] use reader where possible --- library/systemd/src/lib/yast2/compound_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 4625bccd5..8669a9998 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -153,13 +153,13 @@ def start_mode=(configuration) # returns true if any allow start on demand def support_start_on_demand? - @services.any?(&:support_start_on_demand?) + services.any?(&:support_start_on_demand?) end # @return [Hash] Errors when trying to write changes to the # underlying system. def errors - @services.each_with_object({}) do |s, result| + services.each_with_object({}) do |s, result| result.merge!(s.errors) end end From 43a9ffd755e3cdfbadc27676c446a664a389aac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Fri, 27 Jul 2018 08:39:18 +0100 Subject: [PATCH 071/223] Fix query to get value of `autostart` and `action` Since they are widgets of SelectBox type, their selected values should be retrieved asking for :Value instead of :CurrentItem --- library/systemd/src/lib/yast2/service_widget.rb | 6 +++--- library/systemd/test/yast2/service_widget_test.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 12763a54b..4b91d115c 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -109,12 +109,12 @@ def store store_autostart end - private + private attr_reader :service def store_action - action = Yast::UI.QueryWidget(Id(:service_widget_action), :CurrentItem) + action = Yast::UI.QueryWidget(Id(:service_widget_action), :Value) return unless action action = action.to_s.sub(/^service_widget_action_/, "").to_sym @@ -124,7 +124,7 @@ def store_action end def store_autostart - autostart = Yast::UI.QueryWidget(Id(:service_widget_autostart), :CurrentItem) + autostart = Yast::UI.QueryWidget(Id(:service_widget_autostart), :Value) return unless autostart autostart = autostart.to_s.sub(/^service_widget_autostart_/, "").to_sym diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb index 8b6e1b66c..5360503b6 100755 --- a/library/systemd/test/yast2/service_widget_test.rb +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -64,7 +64,7 @@ end it "calls action according to widget" do - allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_action), :CurrentItem) + allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_action), :Value) .and_return(:service_widget_action_restart) expect(service).to receive(:restart) @@ -73,7 +73,7 @@ end it "sets start_mode according to widget" do - allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_autostart), :CurrentItem) + allow(Yast::UI).to receive(:QueryWidget).with(Id(:service_widget_autostart), :Value) .and_return(:service_widget_autostart_manual) expect(service).to receive(:start_mode=).with(:manual) From 47dbe5cb76b1d395f88b403c9f668f25576dcd52 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 27 Jul 2018 11:35:15 +0200 Subject: [PATCH 072/223] fix filtering --- library/systemd/src/lib/yast2/service_widget.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 4b91d115c..716493357 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -162,8 +162,8 @@ def action_items res = [] res << Item(Id(:service_widget_action_start), _("Start"), current_action == :start) if service.current_active? != true res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) if service.current_active? != false - res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) if service.current_active? != true - res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service.current_active? != true && service.support_reload? + res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) if service.current_active? != false + res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service.current_active? != false && service.support_reload? res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action.nil?) res From 1f73b08d27e85b9298707060efcba69a31b2413b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Fri, 27 Jul 2018 09:43:19 +0100 Subject: [PATCH 073/223] Make Rubocop happy again --- library/systemd/src/lib/yast2/service_widget.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 716493357..5349dc917 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -109,7 +109,7 @@ def store store_autostart end - private + private attr_reader :service From dd4764405ae1893430b64bebdb3537531a44cd10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Tue, 31 Jul 2018 08:48:45 +0100 Subject: [PATCH 074/223] Add an id for service status in ServiceWidget Necessary to query and refresh it. --- library/systemd/src/lib/yast2/service_widget.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 5349dc917..9af667dc7 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -80,7 +80,7 @@ def content HBox( Label(_("Current status:")), Label(" "), - Label(status) + Label(Id(:service_widget_status), status) ) ), Left(action_widget), From 7946c325e05b3dc5b6b5e423badce507c266dd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Tue, 31 Jul 2018 08:50:03 +0100 Subject: [PATCH 075/223] Increase tests for ServiceWidget#content --- library/systemd/test/test_helper.rb | 15 +++++++++++++++ .../systemd/test/yast2/service_widget_test.rb | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/library/systemd/test/test_helper.rb b/library/systemd/test/test_helper.rb index df59e7b8a..618c23e2e 100644 --- a/library/systemd/test/test_helper.rb +++ b/library/systemd/test/test_helper.rb @@ -6,6 +6,21 @@ Yast.import "SystemdService" Yast.import "SystemdTarget" +# Find a Term in given content +# +# @param content [Yast::Term] the content in which perform the search +# @param type [Symbol] wanted Term type +# @param id [Symbol] wanted Term id +# +# @return [Yast::Term, nil] a Term when found; nil otherwise +def find_term(content, type, id) + content.nested_find do |term| + next unless term.is_a?(Yast::Term) && term.value == type + + term.params.select { |i| i.is_a?(Yast::Term) && i.params == [id] } + end +end + module SystemctlStubs def stub_systemctl(unit) case unit diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb index 5360503b6..68020d56e 100755 --- a/library/systemd/test/yast2/service_widget_test.rb +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -50,6 +50,24 @@ it "returns Term" do expect(subject.content).to be_a(Yast::Term) end + + it "includes status label" do + status_label = find_term(subject.content, :Label, :service_widget_status) + + expect(status_label).to_not be_nil + end + + it "includes action selector" do + action_selector = find_term(subject.content, :ComboBox, :service_widget_action) + + expect(action_selector).to_not be_nil + end + + it "includes start mode selector" do + autostart_selector = find_term(subject.content, :ComboBox, :service_widget_autostart) + + expect(autostart_selector).to_not be_nil + end end describe "#store" do From 0734c35d5610fe0bb150f81f9bc25e3683bda85e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Tue, 31 Jul 2018 10:14:25 +0100 Subject: [PATCH 076/223] Add a ServiceWidget#refresh method Useful to refresh the widget in certain scenarios, i.e. when the "Apply changes" action is performed.# --- library/cwm/src/lib/cwm/service_widget.rb | 4 ++++ .../systemd/src/lib/yast2/service_widget.rb | 6 +++++ .../systemd/test/yast2/service_widget_test.rb | 24 +++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/library/cwm/src/lib/cwm/service_widget.rb b/library/cwm/src/lib/cwm/service_widget.rb index 468d135a2..5679801e3 100644 --- a/library/cwm/src/lib/cwm/service_widget.rb +++ b/library/cwm/src/lib/cwm/service_widget.rb @@ -36,6 +36,10 @@ def contents @service_widget.content end + def refresh + @service_widget.refresh + end + def handle(event) log.info "handling event #{event.inspect}" return unless event diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 9af667dc7..f90b7b0c8 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -89,6 +89,12 @@ def content ) end + def refresh + Yast::UI.ChangeWidget(Id(:service_widget_status), :Value, status) + Yast::UI.ChangeWidget(Id(:service_widget_action), :Items, action_items) + Yast::UI.ChangeWidget(Id(:service_widget_autostart), :Items, autostart_items) + end + # handles event to dynamically react on user configuration. # For events that does not happen inside widget it is ignored. # @param event_id [Object] id of UI element that cause event diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb index 68020d56e..82fc3d6a9 100755 --- a/library/systemd/test/yast2/service_widget_test.rb +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -70,6 +70,30 @@ end end + describe "#refresh" do + before do + allow(Yast::UI).to receive(:ChangeWidget).with(any_args) + end + + it "updates the status" do + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:service_widget_status), :Value, anything) + + subject.refresh + end + + it "updates available actions" do + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:service_widget_action), :Items, anything) + + subject.refresh + end + + it "updates available options for start mode" do + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:service_widget_autostart), :Items, anything) + + subject.refresh + end + end + describe "#store" do before do allow(Yast::UI).to receive(:QueryWidget) From ab4be7895b8dcddc2cc16740cc21d86d9f7e5d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Thu, 2 Aug 2018 16:34:29 +0100 Subject: [PATCH 077/223] Fix calls to Yast2::SystemService#currently_active? Formerly #current_active?, before improvements introduced in commit 94752cab --- library/systemd/src/lib/yast2/compound_service.rb | 6 +++--- library/systemd/src/lib/yast2/service_widget.rb | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 8669a9998..d4cf29f02 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -61,9 +61,9 @@ def save(keep_state: false) # - `true` when all services are active # - `false` when all services are inactive # - `:inconsistent` when part of services is active and part not. - def current_active? - return true if services.all?(&:current_active?) - return false if services.none?(&:current_active?) + def currently_active? + return true if services.all?(&:currently_active?) + return false if services.none?(&:currently_active?) :inconsistent end diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index f90b7b0c8..b8bb4e071 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -140,7 +140,7 @@ def store_autostart end def status - case service.current_active? + case service.currently_active? # TRANSLATORS: Status of service when true _("Active") @@ -151,7 +151,7 @@ def status # TRANSLATORS: Status of service _("Partly Active") else - raise "Unknown status #{service.current_active?.inspect}" + raise "Unknown status #{service.currently_active?.inspect}" end end @@ -166,10 +166,10 @@ def action_widget def action_items current_action = service.action res = [] - res << Item(Id(:service_widget_action_start), _("Start"), current_action == :start) if service.current_active? != true - res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) if service.current_active? != false - res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) if service.current_active? != false - res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service.current_active? != false && service.support_reload? + res << Item(Id(:service_widget_action_start), _("Start"), current_action == :start) if service.currently_active? != true + res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) if service.currently_active? != false + res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) if service.currently_active? != false + res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service.currently_active? != false && service.support_reload? res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action.nil?) res From 33b5391fda626ffaf480b5ab2c17787379800952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Mon, 6 Aug 2018 15:26:36 +0100 Subject: [PATCH 078/223] Fix rebase --- library/system/test/Makefile.am | 3 +- .../system/test/yast2/system_service_test.rb | 524 ------------------ library/systemd/src/lib/yast2/systemd_unit.rb | 10 - library/systemd/test/yast2/Makefile.am | 9 - 4 files changed, 1 insertion(+), 545 deletions(-) delete mode 100755 library/system/test/yast2/system_service_test.rb delete mode 100644 library/systemd/test/yast2/Makefile.am diff --git a/library/system/test/Makefile.am b/library/system/test/Makefile.am index 4f669eed1..3b54c3128 100644 --- a/library/system/test/Makefile.am +++ b/library/system/test/Makefile.am @@ -4,8 +4,7 @@ TESTS = \ hw_detection_test.rb \ fs_snapshot_test.rb \ fs_snapshot_store_test.rb \ - proc_cmdline_test.rb \ - yast2/system_service_test.rb + proc_cmdline_test.rb TEST_EXTENSIONS = .rb RB_LOG_COMPILER = rspec diff --git a/library/system/test/yast2/system_service_test.rb b/library/system/test/yast2/system_service_test.rb deleted file mode 100755 index f2cc3c9f6..000000000 --- a/library/system/test/yast2/system_service_test.rb +++ /dev/null @@ -1,524 +0,0 @@ -#!/usr/bin/env rspec -# encoding: utf-8 - -# Copyright (c) [2018] 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_relative "../test_helper" -require "yast2/system_service" - -describe Yast2::SystemService do - subject(:system_service) { described_class.new(service) } - - let(:service) do - double("service", enabled?: true, name: "cups", active?: active?, enable: nil) - end - let(:socket) { double("socket", enabled?: true) } - let(:active?) { true } - - before do - allow(service).to receive(:socket).and_return(socket) - end - - describe ".find" do - let(:systemd_service) { instance_double(Yast::SystemdServiceClass::Service) } - - before do - allow(Yast::SystemdService).to receive(:find).with("cups").and_return(systemd_service) - end - - it "finds a systemd service" do - system_service = described_class.find("cups") - expect(system_service).to be_a(described_class) - expect(system_service.service).to eq(systemd_service) - end - end - - describe ".find_many" do - let(:apparmor) { instance_double(Yast::SystemdServiceClass::Service) } - let(:cups) { instance_double(Yast::SystemdServiceClass::Service) } - - before do - allow(Yast::SystemdService).to receive(:find_many).with(["apparmor", "cups"]) - .and_return([apparmor, cups]) - end - - it "finds a set of systemd services" do - system_services = described_class.find_many(["apparmor", "cups"]) - expect(system_services).to be_all(Yast2::SystemService) - expect(system_services.map(&:service)).to eq([apparmor, cups]) - end - - context "when some service is not found" do - before do - allow(Yast::SystemdService).to receive(:find_many).with(["apparmor", "cups"]) - .and_return([nil, cups]) - end - - it "ignores the not found service" do - system_services = described_class.find_many(["apparmor", "cups"]) - expect(system_services.map(&:service)).to eq([cups]) - end - end - end - - describe "#start_mode" do - context "when the service is enabled" do - it "returns :on_boot" do - expect(system_service.start_mode).to eq(:on_boot) - end - end - - context "when the service is disabled" do - let(:service) { double("service", enabled?: false) } - - context "but the associated socket is enabled" do - it "returns :on_demand" do - expect(system_service.start_mode).to eq(:on_demand) - end - end - - context "and the socket is disabled" do - let(:socket) { double("socket", enabled?: false) } - - it "returns :manual" do - expect(system_service.start_mode).to eq(:manual) - end - end - - context "and there is no socket" do - let(:socket) { nil } - - it "returns :manual" do - expect(system_service.start_mode).to eq(:manual) - end - end - end - end - - describe "#start_mode=" do - before do - allow(service).to receive(:enable) - end - - it "sets the wanted start_mode" do - expect { system_service.start_mode = :on_demand }.to change { system_service.start_mode } - .from(:on_boot).to(:on_demand) - end - - context "when an invalid value is given" do - it "raises an error" do - expect { system_service.start_mode = :other }.to raise_error(ArgumentError) - end - end - - context "when the wanted start_mode is the same than the current one" do - before do - system_service.start_mode = :on_demand - end - - it "ignores the change" do - system_service.start_mode = :on_boot - expect(system_service.changed?).to eq(false) - end - end - end - - describe "#start_modes" do - before do - allow(service).to receive(:socket).and_return(socket) - end - - context "when an associated socket exists" do - let(:socket) { double("socket", disable: true) } - - it "returns :on_boot, :on_demand and :manual" do - expect(system_service.start_modes).to eq([:on_boot, :manual, :on_demand]) - end - end - - context "when no associated socket exists" do - let(:socket) { nil } - - it "returns :on_boot and :manual" do - expect(system_service.start_modes).to eq([:on_boot, :manual]) - end - end - end - - describe "#start" do - let(:active?) { false } - - it "sets the service to be active" do - expect { system_service.start }.to change { system_service.active? } - .from(false).to(true) - end - - it "sets the service as changed" do - expect { system_service.start }.to change { system_service.changed_value?(:active) } - .from(false).to(true) - end - - context "and the service is already started (active)" do - let(:active?) { true } - - it "sets the service to stay as active" do - expect { system_service.start }.to_not change { system_service.active? } - end - - it "ignores the change" do - system_service.start - expect(system_service.changed?).to eq(false) - end - end - - context "and the service was set to be stopped" do - let(:active?) { true } - - before do - system_service.stop - end - - it "sets the service to stay as active" do - expect { system_service.start }.to change { system_service.active? } - .from(false).to(true) - end - - it "cancels the previous change" do - system_service.start - expect(system_service.changed?).to eq(false) - end - end - end - - describe "#stop" do - let(:active?) { true } - - it "sets the service to be deactivated" do - expect { system_service.stop }.to change { system_service.active? } - .from(true).to(false) - end - - it "sets the service as changed" do - expect { system_service.stop }.to change { system_service.changed_value?(:active) } - .from(false).to(true) - end - - context "and the service is already stopped (inactive)" do - let(:active?) { false } - - it "sets the service to stay as inactive" do - expect { system_service.stop }.to_not change { system_service.active? } - end - - it "ignores the change" do - system_service.stop - expect(system_service.changed?).to eq(false) - end - end - - context "and the services was set to be stopped" do - let(:active?) { false } - - before do - system_service.start - end - - it "sets the service to stay as active" do - expect { system_service.stop }.to change { system_service.active? } - .from(true).to(false) - end - - it "cancels the previous change" do - system_service.stop - expect(system_service.changed?).to eq(false) - end - end - end - - describe "#toggle" do - context "when the service is set to be started" do - before do - system_service.start - end - - it "sets the services to be stopped" do - expect { system_service.toggle }.to change { system_service.active? } - .from(true).to(false) - end - end - - context "when the service is set to be stopped" do - before do - system_service.stop - end - - it "sets the services to be started" do - expect { system_service.toggle }.to change { system_service.active? } - .from(false).to(true) - end - end - end - - describe "#active?" do - context "when the underlying service is active" do - let(:active?) { true } - - it "returns true" do - expect(system_service.active?).to eq(true) - end - end - - context "when the underlying service is inactive" do - let(:active?) { false } - - it "returns false" do - expect(system_service.active?).to eq(false) - end - end - - context "when service was set to be started" do - before do - system_service.start - end - - it "returns true" do - expect(system_service.active?).to eq(true) - end - end - - context "when service was set to be stopped" do - before do - system_service.stop - end - - it "returns false" do - expect(system_service.active?).to eq(false) - end - end - end - - describe "#save=" do - let(:socket) { double("socket", disable: true) } - let(:start_mode) { :on_boot } - - before do - system_service.start_mode = start_mode - end - - context "when start_mode was changed to :on_boot" do - let(:service) { double("service", enabled?: false, name: "cups") } - let(:socket) { double("socket", enabled?: true) } - let(:start_mode) { :on_boot } - - it "enables the service to start on boot" do - expect(service).to receive(:enable).and_return(true) - expect(socket).to receive(:disable).and_return(true) - system_service.save - end - end - - context "when start_mode was changed to :on_demand" do - let(:start_mode) { :on_demand } - - it "enables the socket" do - expect(service).to receive(:disable).and_return(true) - expect(socket).to receive(:enable).and_return(true) - system_service.save - end - end - - context "when start_mode was changed to :manual" do - let(:start_mode) { :manual } - - it "disables the service and the socket" do - expect(service).to receive(:disable).and_return(true) - expect(socket).to receive(:disable).and_return(true) - system_service.save - end - end - - context "when setting the start_mode fails" do - let(:start_mode) { :on_demand } - - before do - allow(service).to receive(:disable).and_return(false) - end - - it "registers the error" do - system_service.save - expect(system_service.errors).to eq(start_mode: :on_demand) - end - end - - context "when the service is set to be started" do - before { system_service.start } - - context "and the service is already active" do - let(:active?) { true } - - it "does not try to activate the service again" do - expect(service).to_not receive(:start) - system_service.save - end - end - - context "and the service is inactive" do - let(:active?) { false } - - it "tries to activate the service" do - expect(service).to receive(:start).and_return(true) - system_service.save - end - - context "and the service is successfully started" do - it "does not register any error" do - allow(service).to receive(:start).and_return(true) - system_service.save - expect(system_service.errors).to_not have_key(:active) - end - end - - context "and the service could not be started" do - before do - allow(service).to receive(:start).and_return(false) - end - - it "registers the error" do - system_service.save - expect(system_service.errors).to eq(active: true) - end - end - - context "and the status must be ignored" do - it "does not try to activate the service" do - expect(service).to_not receive(:start) - system_service.save(ignore_status: true) - end - end - end - end - - context "when the service is set to be stopped" do - before { system_service.stop } - - context "and the service is active" do - let(:active?) { true } - - it "tries to stop the service" do - expect(service).to receive(:stop) - system_service.save - end - - context "and the service is successfully stopped" do - it "does not register any error" do - allow(service).to receive(:stop).and_return(true) - system_service.save - expect(system_service.errors).to_not have_key(:active) - end - end - - context "and the service could not be stopped" do - before do - allow(service).to receive(:stop).and_return(false) - end - - it "registers the error" do - system_service.save - expect(system_service.errors).to eq(active: false) - end - end - - context "and the status must be ignored" do - it "does not try to stop the service" do - expect(service).to_not receive(:start) - system_service.save(ignore_status: true) - end - end - end - - context "and the service is already inactive" do - let(:active?) { false } - - it "does not try to stop the service again" do - expect(service).to_not receive(:stop) - system_service.save - end - end - end - end - - describe "#changed?" do - context "when some change was made" do - before do - system_service.stop - end - - it "returns true" do - expect(system_service.changed?).to eq(true) - end - end - - context "when no changes were made" do - it "returns false" do - expect(system_service.changed?).to eq(false) - end - end - end - - describe "#search_terms" do - before do - allow(service).to receive(:id).and_return("cups.service") - end - - context "when the service does not have an associated socket" do - let(:socket) { nil } - - it "returns only the service full name" do - expect(system_service.search_terms).to contain_exactly("cups.service") - end - end - - context "when the service has an associated socket" do - let(:socket) { instance_double(Yast::SystemdSocketClass::Socket, id: "cups.socket") } - - it "returns the service and socket full names" do - expect(system_service.search_terms).to contain_exactly("cups.service", "cups.socket") - end - end - end - - describe "#changed_value?" do - context "when no value has been changed" do - it "returns true" do - expect(system_service.changed_value?(:active)).to eq(false) - end - end - - context "when some value has been changed" do - before do - system_service.stop - end - - it "returns true" do - expect(system_service.changed_value?(:active)).to eq(true) - end - end - end -end diff --git a/library/systemd/src/lib/yast2/systemd_unit.rb b/library/systemd/src/lib/yast2/systemd_unit.rb index ddd3fe7ff..c358b0d2f 100644 --- a/library/systemd/src/lib/yast2/systemd_unit.rb +++ b/library/systemd/src/lib/yast2/systemd_unit.rb @@ -252,7 +252,6 @@ def initialize(systemd_unit, property_text) end extract_properties -<<<<<<< HEAD self[:active?] = ACTIVE_STATES.include?(active_state) self[:running?] = sub_state == "running" self[:loaded?] = load_state == "loaded" @@ -261,15 +260,6 @@ def initialize(systemd_unit, property_text) self[:enabled?] = read_enabled_state self[:supported?] = SUPPORTED_STATES.include?(unit_file_state) self[:can_reload?] = can_reload == "yes" -======= - self[:active?] = ACTIVE_STATES.include?(active_state) - self[:running?] = sub_state == "running" - self[:loaded?] = load_state == "loaded" - self[:not_found?] = load_state == "not-found" - self[:static?] = load_state == "static" - self[:enabled?] = read_enabled_state - self[:supported?] = SUPPORTED_STATES.include?(unit_file_state) ->>>>>>> Add SystemdUnit#static? end private diff --git a/library/systemd/test/yast2/Makefile.am b/library/systemd/test/yast2/Makefile.am deleted file mode 100644 index 1a2d476a1..000000000 --- a/library/systemd/test/yast2/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -TESTS = \ - yast2/service_configuration_test.rb - -TEST_EXTENSIONS = .rb -RB_LOG_COMPILER = rspec -VERBOSE = 1 - -EXTRA_DIST = $(TESTS) - From 493e32763fe96de5bcf028e8fa8da34cf4e04b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Mon, 6 Aug 2018 15:52:43 +0100 Subject: [PATCH 079/223] Fix CompoundService unit test The former #current_active? method changed to #currently_active? but it was forgotten to update the test in commit ab4be7895 --- .../systemd/test/yast2/compound_service_test.rb | 14 +++++++------- library/systemd/test/yast2/service_widget_test.rb | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/systemd/test/yast2/compound_service_test.rb b/library/systemd/test/yast2/compound_service_test.rb index 0890106fa..9cf6d2943 100755 --- a/library/systemd/test/yast2/compound_service_test.rb +++ b/library/systemd/test/yast2/compound_service_test.rb @@ -72,28 +72,28 @@ def service(*args) end end - describe "#current_active?" do + describe "#currently_active?" do context "all services are active" do - subject { described_class.new(service(current_active?: true), service(current_active?: true)) } + subject { described_class.new(service(currently_active?: true), service(currently_active?: true)) } it "returns true" do - expect(subject.current_active?).to eq true + expect(subject.currently_active?).to eq true end end context "all services are inactive" do - subject { described_class.new(service(current_active?: false), service(current_active?: false)) } + subject { described_class.new(service(currently_active?: false), service(currently_active?: false)) } it "returns false" do - expect(subject.current_active?).to eq false + expect(subject.currently_active?).to eq false end end context "some services are active and some inactive" do - subject { described_class.new(service(current_active?: false), service(current_active?: true)) } + subject { described_class.new(service(currently_active?: false), service(currently_active?: true)) } it "returns :inconsistent" do - expect(subject.current_active?).to eq :inconsistent + expect(subject.currently_active?).to eq :inconsistent end end end diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb index 82fc3d6a9..0f04985e2 100755 --- a/library/systemd/test/yast2/service_widget_test.rb +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -28,7 +28,7 @@ let(:service) do double( "Yast2::SystemService", - current_active?: true, + currently_active?: true, start_mode: :on_boot, current_start_mode: :on_boot ).as_null_object From 88ef789cc1b9720ac57b4936809467a4cf25d464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Mon, 6 Aug 2018 16:05:49 +0100 Subject: [PATCH 080/223] Add documentation for ServiceWidget#refresh --- library/systemd/src/lib/yast2/service_widget.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index b8bb4e071..92b5192fd 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -89,10 +89,16 @@ def content ) end + # Updates the widget with current values of service + # + # Useful to update the information after certain actions like "Apply changes" + # + # @return [nil] def refresh Yast::UI.ChangeWidget(Id(:service_widget_status), :Value, status) Yast::UI.ChangeWidget(Id(:service_widget_action), :Items, action_items) Yast::UI.ChangeWidget(Id(:service_widget_autostart), :Items, autostart_items) + nil end # handles event to dynamically react on user configuration. From cd3df63367e782e9dac40d4e62d3a29539f08c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Mon, 6 Aug 2018 16:14:12 +0100 Subject: [PATCH 081/223] Fix Rubocop offense --- library/systemd/test/yast2/service_widget_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb index 0f04985e2..589e691c6 100755 --- a/library/systemd/test/yast2/service_widget_test.rb +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -28,7 +28,7 @@ let(:service) do double( "Yast2::SystemService", - currently_active?: true, + currently_active?: true, start_mode: :on_boot, current_start_mode: :on_boot ).as_null_object From 4f375065dbc6bc0cde980c02fd997c5180bfd75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 8 Aug 2018 09:34:03 +0100 Subject: [PATCH 082/223] Add ServiceWidget#init --- library/cwm/src/lib/cwm/service_widget.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/cwm/src/lib/cwm/service_widget.rb b/library/cwm/src/lib/cwm/service_widget.rb index 5679801e3..73ae4b8a2 100644 --- a/library/cwm/src/lib/cwm/service_widget.rb +++ b/library/cwm/src/lib/cwm/service_widget.rb @@ -50,5 +50,11 @@ def handle(event) def store @service_widget.store end + + # The widget needs to be refreshed each time it is rendered. Otherwise, cached + # service values would not be selected (e.g., when switching in a DialogTree) + def init + refresh + end end end From a72a5d04dda2ee2e9d796d6f35940c2d49647bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= Date: Wed, 8 Aug 2018 09:51:49 +0100 Subject: [PATCH 083/223] Fix :reload action in ServiceWidget#action_items --- library/systemd/src/lib/yast2/service_widget.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 92b5192fd..b3e2e5355 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -175,7 +175,7 @@ def action_items res << Item(Id(:service_widget_action_start), _("Start"), current_action == :start) if service.currently_active? != true res << Item(Id(:service_widget_action_stop), _("Stop"), current_action == :stop) if service.currently_active? != false res << Item(Id(:service_widget_action_restart), _("Restart"), current_action == :restart) if service.currently_active? != false - res << Item(Id(:service_widget_action_restart), _("Reload"), current_action == :reload) if service.currently_active? != false && service.support_reload? + res << Item(Id(:service_widget_action_reload), _("Reload"), current_action == :reload) if service.currently_active? != false && service.support_reload? res << Item(Id(:service_widget_action_nothing), _("Keep current state"), current_action.nil?) res From 81ee715bc30f03d034658adc58e33302aaf20e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 8 Aug 2018 16:10:54 +0100 Subject: [PATCH 084/223] Add missing translations --- library/systemd/src/lib/yast2/service_widget.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index b3e2e5355..14b9af547 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -164,7 +164,7 @@ def status def action_widget ComboBox( Id(:service_widget_action), - "After writting configuration:", + _("After writing configuration:"), action_items ) end @@ -184,7 +184,7 @@ def action_items def autostart_widget ComboBox( Id(:service_widget_autostart), - "After reboot:", + _("After reboot:"), autostart_items ) end From 85cb5edbc12783ebb199c8a2497fd57354182082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 8 Aug 2018 16:15:59 +0100 Subject: [PATCH 085/223] Update version and changelog --- package/yast2.changes | 6 ++++++ package/yast2.spec | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index cae8cf320..70ffeb687 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Aug 8 15:14:29 UTC 2018 - jlopez@suse.com + +- Added widget to configure services (part of fate#319428). +- 4.1.0 + ------------------------------------------------------------------- Wed Aug 8 10:09:55 UTC 2018 - igonzalezsosa@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 8114fd1c6..2a44e6aff 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.83 +Version: 4.1.0 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From e3a17ab182ffe7ee6d381b3fc2f02af6e01697d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Thu, 9 Aug 2018 09:02:31 +0100 Subject: [PATCH 086/223] Improve documentation --- library/systemd/examples/service_widget.rb | 5 +- .../systemd/src/lib/yast2/compound_service.rb | 178 ++++++++++++------ 2 files changed, 124 insertions(+), 59 deletions(-) diff --git a/library/systemd/examples/service_widget.rb b/library/systemd/examples/service_widget.rb index a45dfec45..351ebea03 100644 --- a/library/systemd/examples/service_widget.rb +++ b/library/systemd/examples/service_widget.rb @@ -6,9 +6,12 @@ def service return @service if @service - # @service ||= Yast2::SystemService.find("cups.service") + service1 = Yast2::SystemService.find("cups.service") service2 = Yast2::SystemService.find("dbus.service") + + # Yast2::ServiceWidget can be used with both, a Yast2::SystemService or + # a Yast2::CompoundService @service = Yast2::CompoundService.new(service1, service2) end diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index d4cf29f02..eeb6de415 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -20,24 +20,27 @@ # find current contact information at www.suse.com. require "yast" - require "yast2/system_service" module Yast2 # Class that represents several related services that need to be synchronized. - # It mimics behavior of single SystemService, just adds additional possible states. + # It mimics behavior of single {Yast2::SystemService}, but it adds possible states. class CompoundService include Yast::Logger - # managed services + # Managed services + # # @return [Array] attr_reader :services - # creates new configuration that holds state for given services - # @param services [Array] services to configure + # Creates new service composed by several services + # + # @param services [Array] # - # @example three services - # service = Yast2::CompoundService.new(s1, s2, s3) + # @example + # iscsi = Yast2::SystemService.find("iscsi") + # iscsid = Yast2::SystemService.find("iscsid") + # service = Yast2::CompoundService.new(iscsi, iscsid) def initialize(*services) if services.any? { |s| !s.is_a?(Yast2::SystemService) } raise ArgumentError, "Services can be only System Service - #{services.inspect}" @@ -46,96 +49,133 @@ def initialize(*services) @services = services end - # writes services new status - # @return [true,false] returns false if saving any service failed + # Saves all services + # + # @see {Yast2::SystemService#save} + # + # @return [Boolean] true if all services were correctly saved; false otherwise def save(keep_state: false) services.each { |s| s.save(keep_state: keep_state) } errors.empty? end - # returns current running status. - # @return [true, false, :inconsistent] current status of - # services: + # Whether the services are currently active + # + # @return [true, false, :inconsistent] # - # - `true` when all services are active - # - `false` when all services are inactive - # - `:inconsistent` when part of services is active and part not. + # - `true` when all services are active + # + # - `false` when all services are not active + # + # - `:inconsistent` when part of services are active and part not. + # + # @note It offers an additional value `:inconsistent` that is not in {Yast2::SystemService} def currently_active? return true if services.all?(&:currently_active?) return false if services.none?(&:currently_active?) :inconsistent end - # returns true if any service supports reload + # Whether any service supports reload + # + # @see {Yast2::SystemService#support_reload?} + # + # @return [Boolean] def support_reload? services.any?(&:support_reload?) end - # Possible start modes taking into account all services. For example, - # if a service supports on boot and manually, but other service supports - # on demand too, the possible starts modes will the all of them: on boot, - # on demand and manually. + # Possible start modes taking into account all services + # + # If a service supports :on_boot and :manual start modes, but another + # service supports :on_demand too, the possible starts modes will the + # all of them: :on_boot, on_demand and :manual. + # + # @see {Yast2::SystemService#start_modes} # # @return [Array] start modes (:on_boot, :on_demand, :manual) def start_modes @start_modes ||= services.map(&:start_modes).reduce(:+).uniq end - # Keywords that can be used to search for involved underlaying units. + # Keywords that can be used to search for involved underlying units + # + # @see {Yast2::SystemService#keywords} + # + # @return [Array] def keywords services.map(&:keywords).reduce(:+).uniq end - # returns currently set target action. If it is not set it returns - # `nil`. - # @return [:start, :stop, :restart, :reload, nil] action for all services: + # Action to perform over all services + # + # @see Yast2::SystemService#action + # + # @return [:start, :stop, :restart, :reload, nil] + # + # - `:start` starts all services. If service is already active, do nothing. + # if service has socket it starts socket instead of service. # - # - `:start` to start all services. If service is already active, do nothing. - # if services has socket it starts socket instead of service. - # - `:stop` to stop all services. If service already is inactive, do nothing. - # - `:restart` restart all services. If service is inactive, it is started. - # - `:reload` reload all services that support it and restart that does not - # support it. If service is inactive, it is started. - # - `nil` do not touch anything. + # - `:stop` stops all services. If service already is inactive, do nothing. + # + # - `:restart` restarts all services. If service is inactive, it is started. + # + # - `:reload` reloads all services that support it and restarts that does not + # support it. If service is inactive, it is started. + # + # - `nil` no action has been indicated. Does nothing. def action # TODO: check for inconsistencies? services.first.action end - # returns current system start mode. - # @return [:on_boot, :on_demand, :manual, :inconsistent] start_mode configuration: + # Current start mode in the system + # + # @note It offers an additional state `:inconsistent` that is not in {Yast2::SystemService} + # + # @see Yast2::SystemService#current_start_mode # - # - `:on_boot` all services start during boot. - # - `:on_demand` all sockets associated with services is enabled and - # for services that does not have socket, it starts on boot. - # - `:manual` all services and all its associated sockets are disabled. - # - `:inconsistent` mixture of states - # @see Yast::SystemService#current_start_mode - # @note additional state `:inconsistent` that is not in SystemService + # @return [:on_boot, :on_demand, :manual, :inconsistent] + # + # - `:on_boot` all services start during boot. + # + # - `:on_demand` all sockets associated with services are enabled and + # for services that do not have socket, they starts on boot. + # - `:manual` all services and all their associated sockets are disabled. + # + # - `:inconsistent` mixture of start modes def current_start_mode @current_start_mode ||= services_mode(method(:current_mode?)) end - # returns configuration for start mode. - # @return [:on_boot, :on_demand, :manual, :inconsistent] start_mode configuration: + # Target start mode + # + # @note It offers and additional start mode `:inconsistent` that is not in {Yast2::SystemService} + # + # @see Yast2::SystemService#start_mode + # + # @return [:on_boot, :on_demand, :manual, :inconsistent] + # + # - `:on_boot` when start mode is set to :on_boot for all services. + # + # - `:on_demand` when start mode is set to :on_demand for services that + # support it and :on_boot for the rest. + # + # - `:manual` when start mode is set to :manual for all services. # - # - `:on_boot` when start mode is set to on_boot for all services. - # - `:on_demand` when start mode is set to on_demand for services that support it and on_boot - # otherwise. - # - `:manual` when start mode is set to manual for all services. # - `:inconsistent` when services have mixture of start modes - # @see Yast::SystemService#start_mode - # @note additional state `:inconsistent` that is not in SystemService def start_mode services_mode(method(:mode?)) end AUTOSTART_OPTIONS = [:on_boot, :on_demand, :manual, :inconsistent].freeze - # sets start mode configuration. - # @param [:on_boot, :on_demand, :manual, :inconsistent] sets start_mode. - # See {Yast2::SystemService.start_mode=}. Additionally inconsistent is used - # to keep current start mode. + + # Sets the target start mode + # + # @note It offers and additional start mode `:inconsistent` that is not in {Yast2::SystemService} + # + # @param [Symbol] new start_mode (e.g., :on_boot, :on_demand, :manual, :inconsistent) def start_mode=(configuration) if !AUTOSTART_OPTIONS.include?(configuration) raise ArgumentError, "Invalid parameter #{configuration.inspect}" @@ -151,22 +191,32 @@ def start_mode=(configuration) end end - # returns true if any allow start on demand + # Whether any service allows to start on demand + # + # @see Yast2::SystemService#support_start_on_demand? + # + # @return [Boolean] def support_start_on_demand? services.any?(&:support_start_on_demand?) end - # @return [Hash] Errors when trying to write changes to the - # underlying system. + # Errors when trying to write changes to the underlying system + # + # @see Yast2::SystemService#errors + # + # @return [Hash] def errors services.each_with_object({}) do |s, result| result.merge!(s.errors) end end - # resets changes. - # @param exclude [Array] exclude from reset some parts. Now supported: `:action` and - # `:start_mode`. + # Resets changes + # + # @see Yast2::SystemService#reset + # + # @param exclude [Array] exclude from reset some parts. + # Now supported: `:action` and `:start_mode` def reset(exclude: []) old_action = action old_start_mode = start_mode @@ -176,18 +226,30 @@ def reset(exclude: []) self.start_mode = old_start_mode if old_start_mode != :inconsistent && exclude.include?(:start_mode) end + # Sets all services to be started after calling {#save} + # + # @see Yast2::SystemService#start def start services.each(&:start) end + # Sets all services to be stopped after calling {#save} + # + # @see Yast2::SystemService#stop def stop services.each(&:stop) end + # Sets all services to be restarted after calling {#save} + # + # @see Yast2::SystemService#restart def restart services.each(&:restart) end + # Sets all services to be reloaded after calling {#save} + # + # @see Yast2::SystemService#reload def reload services.each(&:reload) end From a229cf9c2cf4a8cf0ae8bd29759ba802d8dec47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Thu, 9 Aug 2018 09:04:36 +0100 Subject: [PATCH 087/223] Fix texts --- library/systemd/test/yast2/compound_service_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/systemd/test/yast2/compound_service_test.rb b/library/systemd/test/yast2/compound_service_test.rb index 9cf6d2943..90013b42c 100755 --- a/library/systemd/test/yast2/compound_service_test.rb +++ b/library/systemd/test/yast2/compound_service_test.rb @@ -170,7 +170,7 @@ def service(*args) end end - context "services which supports it start on demand and rest start on boot" do + context "services which supports it start on demand and the rest start on boot" do subject do described_class.new( service(current_start_mode: :on_demand, support_start_on_demand?: true), @@ -211,7 +211,7 @@ def service(*args) end end - context "all services are set to not start automatic" do + context "all services are set to not start automatically" do subject do described_class.new( service(start_mode: :manual), @@ -224,7 +224,7 @@ def service(*args) end end - context "services which supports it are started on demand and rest start on boot" do + context "services which supports it are started on demand and the rest start on boot" do subject do described_class.new( service(start_mode: :on_demand, support_start_on_demand?: true), @@ -262,7 +262,7 @@ def service(*args) end context "parameter is :manual" do - it "sets all services to not start automatic" do + it "sets all services to not start automatically" do expect(service1).to receive(:start_mode=).with(:manual) expect(service2).to receive(:start_mode=).with(:manual) @@ -274,7 +274,7 @@ def service(*args) let(:service1) { service(support_start_on_demand?: true) } let(:service2) { service(support_start_on_demand?: false) } - it "sets services that support it start on demand and rest on boot" do + it "sets services that support it start on demand and the rest on boot" do expect(service1).to receive(:start_mode=).with(:on_demand) expect(service2).to receive(:start_mode=).with(:on_boot) From 7936414c2f2083df07bee84859b8e8940a31cbb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Thu, 9 Aug 2018 09:14:34 +0100 Subject: [PATCH 088/223] Fix typos --- .../systemd/src/lib/yast2/compound_service.rb | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index eeb6de415..60d3b8ab4 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -33,7 +33,7 @@ class CompoundService # @return [Array] attr_reader :services - # Creates new service composed by several services + # Creates a new service composed by several services # # @param services [Array] # @@ -66,7 +66,7 @@ def save(keep_state: false) # # - `true` when all services are active # - # - `false` when all services are not active + # - `false` when no services are active # # - `:inconsistent` when part of services are active and part not. # @@ -89,7 +89,7 @@ def support_reload? # Possible start modes taking into account all services # # If a service supports :on_boot and :manual start modes, but another - # service supports :on_demand too, the possible starts modes will the + # service supports :on_demand too, the possible start modes will the # all of them: :on_boot, on_demand and :manual. # # @see {Yast2::SystemService#start_modes} @@ -114,12 +114,12 @@ def keywords # # @return [:start, :stop, :restart, :reload, nil] # - # - `:start` starts all services. If service is already active, do nothing. - # if service has socket it starts socket instead of service. + # - `:start` starts all services. If a service is already active, does nothing. + # if a service has socket it starts socket instead of the service. # - # - `:stop` stops all services. If service already is inactive, do nothing. + # - `:stop` stops all services. If a service is inactive, does nothing. # - # - `:restart` restarts all services. If service is inactive, it is started. + # - `:restart` restarts all services. If a service is inactive, it is started. # # - `:reload` reloads all services that support it and restarts that does not # support it. If service is inactive, it is started. @@ -141,7 +141,8 @@ def action # - `:on_boot` all services start during boot. # # - `:on_demand` all sockets associated with services are enabled and - # for services that do not have socket, they starts on boot. + # for services that do not have socket, they start on boot. + # # - `:manual` all services and all their associated sockets are disabled. # # - `:inconsistent` mixture of start modes @@ -175,7 +176,7 @@ def start_mode # # @note It offers and additional start mode `:inconsistent` that is not in {Yast2::SystemService} # - # @param [Symbol] new start_mode (e.g., :on_boot, :on_demand, :manual, :inconsistent) + # @param [Symbol] new start mode (e.g., :on_boot, :on_demand, :manual, :inconsistent) def start_mode=(configuration) if !AUTOSTART_OPTIONS.include?(configuration) raise ArgumentError, "Invalid parameter #{configuration.inspect}" @@ -215,7 +216,7 @@ def errors # # @see Yast2::SystemService#reset # - # @param exclude [Array] exclude from reset some parts. + # @param exclude [Array] to exclude from reset some parts. # Now supported: `:action` and `:start_mode` def reset(exclude: []) old_action = action From 9f519ec8feb94b87d54c7788cf1acf08abcb8fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 10 Aug 2018 12:04:49 +0100 Subject: [PATCH 089/223] Revert "adapt Rakefile and Dockerfile for SLE-15-GA" This reverts commit 6047c93d6ab96ae5f8dcf04ffd29c226c4f86f36. --- Dockerfile | 2 +- Rakefile | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index b1e712ce9..f9587f00b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # Use this base image # - built: https://hub.docker.com/r/yastdevel/ruby/ # - source: https://github.com/yast/docker-yast-ruby -FROM yastdevel/ruby:sle15 +FROM yastdevel/ruby COPY . /usr/src/app # English messages, UTF-8, "C" locale for numeric formatting tests ENV LC_ALL= LANG=en_US.UTF-8 LC_NUMERIC=C diff --git a/Rakefile b/Rakefile index 45bceb6c9..2432dd52a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,5 @@ require "yast/rake" -Yast::Tasks.submit_to :sle15 - Yast::Tasks.configuration do |conf| # lets ignore license check for now conf.skip_license_check << /.*/ From 65db9f1800b6e691afb0afec805c584a123a41c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 10 Aug 2018 12:15:59 +0100 Subject: [PATCH 090/223] Add missing separator to Makefile.am --- library/systemd/test/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/systemd/test/Makefile.am b/library/systemd/test/Makefile.am index 4b340b87a..3e014ce6a 100644 --- a/library/systemd/test/Makefile.am +++ b/library/systemd/test/Makefile.am @@ -6,7 +6,7 @@ TESTS = \ systemd_service_test.rb \ yast2/compound_service_test.rb \ yast2/service_widget_test.rb \ - yast2/system_service_test.rb + yast2/system_service_test.rb \ yast2/system_service_test.rb \ yast2/systemd_socket_finder_test.rb From d062a0725c188a2f2a32f9846f581e9db997ea62 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Mon, 20 Aug 2018 17:16:30 +0200 Subject: [PATCH 091/223] Changed dir of COPYING file. --- package/yast2.changes | 5 +++++ package/yast2.spec | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index b6659d121..c3358ecae 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Mon Aug 20 17:13:52 CEST 2018 - schubi@suse.de + +- Changed dir of COPYING file + ------------------------------------------------------------------- Tue Aug 14 17:32:02 UTC 2018 - igonzalezsosa@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 11a753294..ed4fdd7ca 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -187,7 +187,7 @@ mkdir -p %{buildroot}%{_sysconfdir}/YaST2 # documentation (not included in devel subpackage) %doc %dir %{yast_docdir} -%doc %{yast_docdir}/COPYING +%license %{yast_docdir}/COPYING %{_mandir}/*/* %doc %{yast_vardir}/hooks/README.md From e4ab10035ba30f739175009344872c4062c26db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= <1691872+dgdavid@users.noreply.github.com> Date: Thu, 23 Aug 2018 11:36:45 +0100 Subject: [PATCH 092/223] Reorganize Yast2 service classes (#799) * Move `SystemdServiceClass` module to real `SystemdService` class * Move `SystemdSocketClass` module to real `SystemdSocket` class * Move `SystemdTargetClass` module to real `SystemdTarget` class * Move systemd classes to Yast2 namespace * Fix Rubocop offenses * Add Yast2::Systemd::Socket.reset to clean the cache for finder * Improve from code review * Extract related Yast2::Systemd::Unit classes to their own files * Update version and changelog * Require missing file --- library/general/src/lib/ui/service_status.rb | 4 +- library/network/src/modules/NetworkService.rb | 4 +- library/runlevel/src/modules/Service.rb | 65 ++-- library/runlevel/test/service_test.rb | 2 +- library/runlevel/test/test_helper.rb | 2 +- .../systemd/examples/comparison_of_widgets.rb | 5 +- library/systemd/examples/service_widget.rb | 4 +- library/systemd/src/Makefile.am | 21 +- .../systemd/src/lib/yast2/system_service.rb | 41 +- library/systemd/src/lib/yast2/systemctl.rb | 23 +- library/systemd/src/lib/yast2/systemd.rb | 7 + .../systemd/src/lib/yast2/systemd/service.rb | 221 +++++++++++ .../systemd/src/lib/yast2/systemd/socket.rb | 109 ++++++ .../src/lib/yast2/systemd/socket_finder.rb | 107 ++++++ .../systemd/src/lib/yast2/systemd/target.rb | 121 ++++++ library/systemd/src/lib/yast2/systemd/unit.rb | 200 ++++++++++ .../systemd/unit_installation_properties.rb | 62 +++ .../src/lib/yast2/systemd/unit_prop_map.rb | 23 ++ .../src/lib/yast2/systemd/unit_properties.rb | 101 +++++ .../src/lib/yast2/systemd_socket_finder.rb | 105 ------ library/systemd/src/lib/yast2/systemd_unit.rb | 354 ------------------ .../systemd/src/modules/systemd_service.rb | 220 ----------- library/systemd/src/modules/systemd_socket.rb | 109 ------ library/systemd/src/modules/systemd_target.rb | 121 ------ library/systemd/test/Makefile.am | 18 +- library/systemd/test/test_helper.rb | 32 +- .../systemd/test/yast2/system_service_test.rb | 32 +- .../test/{ => yast2}/systemctl_test.rb | 14 +- .../test/{ => yast2}/systemd_service_test.rb | 90 +++-- .../test/yast2/systemd_socket_finder_test.rb | 14 +- .../test/{ => yast2}/systemd_socket_test.rb | 39 +- .../test/{ => yast2}/systemd_target_test.rb | 44 ++- .../test/{ => yast2}/systemd_unit_test.rb | 94 ++--- package/yast2.changes | 8 + package/yast2.spec | 2 +- 35 files changed, 1226 insertions(+), 1192 deletions(-) create mode 100644 library/systemd/src/lib/yast2/systemd.rb create mode 100644 library/systemd/src/lib/yast2/systemd/service.rb create mode 100644 library/systemd/src/lib/yast2/systemd/socket.rb create mode 100644 library/systemd/src/lib/yast2/systemd/socket_finder.rb create mode 100644 library/systemd/src/lib/yast2/systemd/target.rb create mode 100644 library/systemd/src/lib/yast2/systemd/unit.rb create mode 100644 library/systemd/src/lib/yast2/systemd/unit_installation_properties.rb create mode 100644 library/systemd/src/lib/yast2/systemd/unit_prop_map.rb create mode 100644 library/systemd/src/lib/yast2/systemd/unit_properties.rb delete mode 100644 library/systemd/src/lib/yast2/systemd_socket_finder.rb delete mode 100644 library/systemd/src/lib/yast2/systemd_unit.rb delete mode 100644 library/systemd/src/modules/systemd_service.rb delete mode 100644 library/systemd/src/modules/systemd_socket.rb delete mode 100644 library/systemd/src/modules/systemd_target.rb rename library/systemd/test/{ => yast2}/systemctl_test.rb (85%) rename library/systemd/test/{ => yast2}/systemd_service_test.rb (67%) rename library/systemd/test/{ => yast2}/systemd_socket_test.rb (63%) rename library/systemd/test/{ => yast2}/systemd_target_test.rb (77%) rename library/systemd/test/{ => yast2}/systemd_unit_test.rb (75%) diff --git a/library/general/src/lib/ui/service_status.rb b/library/general/src/lib/ui/service_status.rb index e8f231423..4a159b742 100644 --- a/library/general/src/lib/ui/service_status.rb +++ b/library/general/src/lib/ui/service_status.rb @@ -34,8 +34,8 @@ class ServiceStatus include Yast::I18n include Yast::Logger - # @param service [Yast::SystemdServiceClass::Service] systemd service. Usually the easiest way - # is just calling `Yast::SystemdService.find("name_of_the_service")` + # @param service [Yast2::Systemd::Service] systemd service. Usually the easiest way + # is just calling `Yast2::Systemd::Service.find("name_of_the_service")` # Note that this widget will #start and #stop the service by itself but # the actions referenced by the flags (reloading and enabling/disabling) # are expected to be done by the caller, when the whole configuration is diff --git a/library/network/src/modules/NetworkService.rb b/library/network/src/modules/NetworkService.rb index bbc0b87a9..0807c1a76 100644 --- a/library/network/src/modules/NetworkService.rb +++ b/library/network/src/modules/NetworkService.rb @@ -42,6 +42,7 @@ # The network.service alias link obsoletes the old master switch in # /etc/sysconfig/network/config:NETWORKMANAGER (until openSUSE-12.2). require "yast" +require "yast2/systemd/service" module Yast class NetworkServiceClass < Module @@ -73,7 +74,6 @@ class NetworkServiceClass < Module include Yast::Logger def main - Yast.import "SystemdService" Yast.import "NetworkConfig" Yast.import "Popup" Yast.import "Mode" @@ -193,7 +193,7 @@ def Read @current_name = DEFAULT_BACKEND log.info "Running in installer/AutoYaST, use default: #{@current_name}" else - service = SystemdService.find("network") + service = Yast2::Systemd::Service.find("network") @current_name = BACKENDS.invert[service.name] if service end diff --git a/library/runlevel/src/modules/Service.rb b/library/runlevel/src/modules/Service.rb index bfd4bb49d..764e206ae 100644 --- a/library/runlevel/src/modules/Service.rb +++ b/library/runlevel/src/modules/Service.rb @@ -31,10 +31,9 @@ # Functions for systemd service handling used by other modules. require "yast" +require "yast2/systemd/service" module Yast - import "SystemdService" - class ServiceClass < Module include Yast::Logger @@ -56,7 +55,7 @@ def initialize # @param [String,String] Command name and service name # @return [Boolean] Result of the action, true means success def call(command_name, service_name) - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) return failure(:not_found, service_name) unless service systemd_command = case command_name @@ -85,7 +84,7 @@ def call(command_name, service_name) # @param [String] name service name # @return true if service is active def Active(service_name) - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) !!(service && service.active?) end @@ -98,7 +97,7 @@ def Active(service_name) # @param [String] name service name # @return true if service is set to run in any runlevel def Enabled(name) - service = SystemdService.find(name) + service = Yast2::Systemd::Service.find(name) !!(service && service.enabled?) end @@ -110,7 +109,7 @@ def Enabled(name) # @return true if operation is successful def Enable(service_name) log.info "Enabling service '#{service_name}'" - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) return failure(:not_found, service_name) unless service return failure(:enable, service_name, service.error) unless service.enable true @@ -124,7 +123,7 @@ def Enable(service_name) # @return true if operation is successful def Disable(service_name) log.info "Disabling service '#{service_name}'" - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) return failure(:not_found, service_name) unless service return failure(:disable, service_name, service.error) unless service.disable true @@ -138,7 +137,7 @@ def Disable(service_name) # @return true if operation is successful def Start(service_name) log.info "Starting service '#{service_name}'" - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) return failure(:not_found, service_name) unless service return failure(:start, service_name, service.error) unless service.start true @@ -152,7 +151,7 @@ def Start(service_name) # @return true if operation is successful def Restart(service_name) log.info "Restarting service '#{service_name}'" - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) return failure(:not_found, service_name) unless service return failure(:restart, service_name, service.error) unless service.restart true @@ -166,7 +165,7 @@ def Restart(service_name) # @return true if operation is successful def Reload(service_name) log.info "Reloading service '#{service_name}'" - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) return failure(:not_found, service_name) unless service return failure(:reload, service_name, service.error) unless service.reload true @@ -180,7 +179,7 @@ def Reload(service_name) # @return true if operation is successful def Stop(service_name) log.info "Stopping service '#{service_name}'" - service = SystemdService.find(service_name) + service = Yast2::Systemd::Service.find(service_name) return failure(:not_found, service_name) unless service return failure(:stop, service_name, service.error) unless service.stop true @@ -198,15 +197,15 @@ def Error error end - # @deprecated Use SystemdService.find + # @deprecated Use Yast2::Systemd::Service.find # Check that a service exists. # If not, set error_msg. # @param [String] name service name without a path, eg. nfsserver # @return Return true if the service exists. def checkExists(name) - deprecate("use `SystemdService.find` instead") + deprecate("use `Yast2::Systemd::Service.find` instead") - return failure(:not_found, name) unless SystemdService.find(name) + return failure(:not_found, name) unless Yast2::Systemd::Service.find(name) true end @@ -217,7 +216,7 @@ def checkExists(name) def Info(name) deprecate("not supported by systemd") - unit = SystemdService.find(name) + unit = Yast2::Systemd::Service.find(name) return {} unless unit read = Convert.to_map(SCR.Read(path(".init.scripts.runlevel"), name)) @@ -231,26 +230,26 @@ def Info(name) ) end - # @deprecated Use SystemdService.find('service_name').id + # @deprecated Use Yast2::Systemd::Service.find('service_name').id # Get complete systemd unit id # @param name name or alias of the unit # @return (resolved) unit Id def GetUnitId(unit) - deprecate("use SystemdService.find('service_name').id") + deprecate("use Yast2::Systemd::Service.find('service_name').id") - unit = SystemdService.find(unit) + unit = Yast2::Systemd::Service.find(unit) return nil unless unit unit.id end - # @deprecated Use SystemdService.find('service_name').name + # @deprecated Use Yast2::Systemd::Service.find('service_name').name # Get the name from a systemd service unit id without the .service suffix # @param [String] name name or alias of the service # @return (resolved) service name without the .service suffix def GetServiceId(name) - deprecate("use SystemdService.find('service_name').name") + deprecate("use Yast2::Systemd::Service.find('service_name').name") - unit = SystemdService.find(name) + unit = Yast2::Systemd::Service.find(name) return nil unless unit unit.name end @@ -264,7 +263,7 @@ def GetServiceId(name) def Status(name) deprecate("use `active?` instead") - unit = SystemdService.find(name) + unit = Yast2::Systemd::Service.find(name) failure(:not_found, name) unless unit unit && unit.active? ? 0 : -1 @@ -291,7 +290,7 @@ def FullInfo(name) def serviceDisable(name, _force) deprecate("use `disable` instead") - unit = SystemdService.find(name) + unit = Yast2::Systemd::Service.find(name) !!(unit && unit.disable) end @@ -306,7 +305,7 @@ def serviceDisable(name, _force) def Adjust(name, action) deprecate("use `enable` or `disable` instead") - service = SystemdService.find(name) + service = Yast2::Systemd::Service.find(name) return failure(:not_found, name) unless service case action @@ -332,7 +331,7 @@ def Adjust(name, action) def Finetune(name, rl) deprecate("use `enable` or `disable` instead") - service = SystemdService.find(name) + service = Yast2::Systemd::Service.find(name) return failure(:not_found, name) unless service if rl.empty? @@ -351,7 +350,7 @@ def Finetune(name, rl) def RunInitScript(name, param) deprecate("use the specific unit command instead") - service = SystemdService.find(name) + service = Yast2::Systemd::Service.find(name) if !service failure(:not_found, name) return -1 @@ -382,7 +381,7 @@ def RunInitScript(name, param) def RunInitScriptWithTimeOut(name, param) deprecate("use `start` or `stop` instead") - service = SystemdService.find(name) + service = Yast2::Systemd::Service.find(name) if !service failure(:not_found, name) return 1 @@ -398,7 +397,7 @@ def RunInitScriptWithTimeOut(name, param) def RunInitScriptOutput(name, param) deprecate("use `start` or `stop` instead") - service = SystemdService.find(name) + service = Yast2::Systemd::Service.find(name) if !service failure(:not_found, name) success = false @@ -414,20 +413,20 @@ def RunInitScriptOutput(name, param) # @param [Fixnum] runlevel requested runlevel number (0-6, -1 = Single) # @return [Array] enabled services def EnabledServices(_runlevel) - deprecate("use `SystemdService.all.select(&:enabled?)`") + deprecate("use `Yast2::Systemd::Service.all.select(&:enabled?)`") - SystemdService.all.select(&:enabled?).map(&:name) + Yast2::Systemd::Service.all.select(&:enabled?).map(&:name) end - # @deprecated Use SystemdService.find instead + # @deprecated Use Yast2::Systemd::Service.find instead # Return the first of the list of services which is available # (has init script) or "" if none is. # @param list list of service names # @return [String] the first found service def Find(services) - deprecate("use `SystemdService.find` instead") + deprecate("use `Yast2::Systemd::Service.find` instead") - services.find { |service_name| SystemdService.find(service_name) } + services.find { |service_name| Yast2::Systemd::Service.find(service_name) } end private diff --git a/library/runlevel/test/service_test.rb b/library/runlevel/test/service_test.rb index 3b6491881..2192d79a5 100755 --- a/library/runlevel/test/service_test.rb +++ b/library/runlevel/test/service_test.rb @@ -9,7 +9,7 @@ module Yast include SystemdServiceStubs def stub_service_with(method, result) - allow_any_instance_of(SystemdServiceClass::Service).to receive(method) + allow_any_instance_of(Yast2::Systemd::Service).to receive(method) .and_return(result) end diff --git a/library/runlevel/test/test_helper.rb b/library/runlevel/test/test_helper.rb index b69b1c0b8..6de88bd6c 100755 --- a/library/runlevel/test/test_helper.rb +++ b/library/runlevel/test/test_helper.rb @@ -1,4 +1,4 @@ -# We need the stubs used for SystemdService tests +# We need the stubs used for Yast2::Systemd::Service tests require_relative "../../systemd/test/test_helper" require_relative "../../../test/test_helper.rb" diff --git a/library/systemd/examples/comparison_of_widgets.rb b/library/systemd/examples/comparison_of_widgets.rb index 70f833990..00918a6f9 100644 --- a/library/systemd/examples/comparison_of_widgets.rb +++ b/library/systemd/examples/comparison_of_widgets.rb @@ -2,12 +2,11 @@ require "yast2/service_widget" require "yast2/service_configuration" +require "yast2/systemd/service" require "ui/service_status" -Yast.import "SystemdService" - def service - @service ||= Yast::SystemdService.find!("cups.service") + @service ||= Yast2::Systemd::Service.find!("cups.service") end def service_configuration diff --git a/library/systemd/examples/service_widget.rb b/library/systemd/examples/service_widget.rb index 351ebea03..c83c05bdf 100644 --- a/library/systemd/examples/service_widget.rb +++ b/library/systemd/examples/service_widget.rb @@ -7,8 +7,8 @@ def service return @service if @service - service1 = Yast2::SystemService.find("cups.service") - service2 = Yast2::SystemService.find("dbus.service") + service1 = Yast2::SystemService.find!("cups.service") + service2 = Yast2::SystemService.find!("dbus.service") # Yast2::ServiceWidget can be used with both, a Yast2::SystemService or # a Yast2::CompoundService diff --git a/library/systemd/src/Makefile.am b/library/systemd/src/Makefile.am index 0e37a41c0..08a63f219 100644 --- a/library/systemd/src/Makefile.am +++ b/library/systemd/src/Makefile.am @@ -1,17 +1,22 @@ -module_DATA = \ - modules/systemd_socket.rb \ - modules/systemd_service.rb \ - modules/systemd_target.rb - ylibdir = @ylibdir@/yast2 ylib_DATA = \ lib/yast2/compound_service.rb \ lib/yast2/service_widget.rb \ lib/yast2/systemctl.rb \ lib/yast2/system_service.rb \ - lib/yast2/systemd_unit.rb \ - lib/yast2/systemd_socket_finder.rb + lib/yast2/systemd.rb + +systemddir = @ylibdir@/yast2/systemd +systemd_DATA = \ + lib/yast2/systemd/unit.rb \ + lib/yast2/systemd/unit_prop_map.rb \ + lib/yast2/systemd/unit_properties.rb \ + lib/yast2/systemd/unit_installation_properties.rb \ + lib/yast2/systemd/service.rb \ + lib/yast2/systemd/socket.rb \ + lib/yast2/systemd/target.rb \ + lib/yast2/systemd/socket_finder.rb -EXTRA_DIST = $(module_DATA) $(ylib_DATA) +EXTRA_DIST = $(ylib_DATA) $(systemd_DATA) include $(top_srcdir)/Makefile.am.common diff --git a/library/systemd/src/lib/yast2/system_service.rb b/library/systemd/src/lib/yast2/system_service.rb index be7450b83..445d38bdb 100644 --- a/library/systemd/src/lib/yast2/system_service.rb +++ b/library/systemd/src/lib/yast2/system_service.rb @@ -20,8 +20,7 @@ # find current contact information at www.suse.com. require "forwardable" - -Yast.import "SystemdService" +require "yast2/systemd/service" module Yast2 # This class represents a service from a high level point of view @@ -84,7 +83,7 @@ class SystemService # Error when a service is not found class NotFoundError < RuntimeError; end - # @return [Yast::SystemdServiceClass::Service] + # @return [Yast2::Systemd::Service] attr_reader :service # @return [Hash] Errors when trying to write changes to the underlying system. @@ -102,16 +101,16 @@ class NotFoundError < RuntimeError; end def_delegator :@service, :can_reload?, :support_reload? # @!method name - # @see Yast::SystemdServiceClass::Service#name + # @see Yast2::Systemd::Service#name # @return [String] # @!method static? - # @see Yast::SystemdServiceClass::Service#static? + # @see Yast2::Systemd::Service#static? # @return [Boolean] # @!method running? - # @see Yast::SystemdServiceClass::Service#running? + # @see Yast2::Systemd::Service#running? # @return [Boolean] # @!method description - # @see Yast::SystemdServiceClass::Service#description + # @see Yast2::Systemd::Service#description # @return [String] def_delegators :@service, :name, :static?, :running?, :description @@ -121,7 +120,7 @@ class << self # @param name [String] service name with or without extension (e.g., "cups" or "cups.service") # @return [SystemService, nil] nil if the service is not found def find(name) - systemd_service = Yast::SystemdService.find(name) + systemd_service = Yast2::Systemd::Service.find(name) return nil unless systemd_service new(systemd_service) @@ -145,9 +144,9 @@ def find!(name) # @param name [String] Service name # @return [SystemService] System service based on the given name # - # @see Yast::SystemdServiceClass#build + # @see Yast2::Systemd::Service.build def build(name) - new(Yast::SystemdService.build(name)) + new(Yast2::Systemd::Service.build(name)) end # Finds a set of services by their names @@ -156,13 +155,13 @@ def build(name) # # @return [Array] def find_many(names) - Yast::SystemdService.find_many(names).map { |s| new(s) } + Yast2::Systemd::Service.find_many(names).map { |s| new(s) } end end # Constructor # - # @param service [Yast::SystemdServiceClass::Service] + # @param service [Yast2::Systemd::Service] def initialize(service) @service = service @changes = {} @@ -345,7 +344,7 @@ def reload # @note All cached changes are reset and the underlying service is refreshed # when the changes are correctly applied. # - # @raise [Yast::SystemctlError] if the service cannot be refreshed + # @raise [Yast2::Systemctl::Error] if the service cannot be refreshed # # @param keep_state [Boolean] Do not change service status. Useful when running on 1st stage. # @return [Boolean] true if the service was saved correctly; false otherwise. @@ -375,13 +374,13 @@ def reset # @return [Boolean] true if the service was refreshed correctly; false otherwise. def refresh refresh! - rescue Yast::SystemctlError + rescue Yast2::Systemctl::Error false end # Refreshes the underlying service # - # @raise [Yast::SystemctlError] if the service cannot be refreshed + # @raise [Yast2::Systemctl::Error] if the service cannot be refreshed # # @return [Boolean] true if the service was refreshed correctly def refresh! @@ -459,14 +458,14 @@ def perform_action # FIXME: SystemdService#{start, stop, etc} calls to refresh! internally, so when # this exception is raised we cannot distinguish if the action is failing or # refresh! is failing. For SP1, refresh! should raise a new kind of exception. - rescue Yast::SystemctlError + rescue Yast2::Systemctl::Error register_error(:active) false end # Starts the service in the underlying system # - # @raise [Yast::SystemctlError] if some service command fails + # @raise [Yast2::Systemctl::Error] if some service command fails # # @return [Boolean] true if the service was correctly started def perform_start @@ -483,7 +482,7 @@ def perform_start # Stops the service in the underlying system # - # @raise [Yast::SystemctlError] if some service command fails + # @raise [Yast2::Systemctl::Error] if some service command fails # # @return [Boolean] true if the service was correctly stopped def perform_stop @@ -497,7 +496,7 @@ def perform_stop # Restarts the service in the underlying system # - # @raise [Yast::SystemctlError] if some service command fails + # @raise [Yast2::Systemctl::Error] if some service command fails # # @return [Boolean] true if the service was correctly restarted def perform_restart @@ -508,7 +507,7 @@ def perform_restart # # @note The service is simply restarted when it does not support reload action. # - # @raise [Yast::SystemctlError] if some service command fails + # @raise [Yast2::Systemctl::Error] if some service command fails # # @return [Boolean] true if the service was correctly reloaded def perform_reload @@ -538,7 +537,7 @@ def clear_errors # Returns the associated socket # - # @return [Yast::SystemdSocketClass::Socket] + # @return [Yast2::Systemd::Socket] def socket service && service.socket end diff --git a/library/systemd/src/lib/yast2/systemctl.rb b/library/systemd/src/lib/yast2/systemctl.rb index 5f3eba610..e33b67fb7 100644 --- a/library/systemd/src/lib/yast2/systemctl.rb +++ b/library/systemd/src/lib/yast2/systemctl.rb @@ -1,19 +1,20 @@ require "ostruct" require "timeout" -module Yast - # Exception when systemctl command failed - class SystemctlError < StandardError - # @param details [#to_s] - def initialize(details) - super "Systemctl command failed: #{details}" - end - end - +module Yast2 # Wrapper around `systemctl` command. # - uses non-interactive flags # - has a timeout + # module Systemctl + # Exception when systemctl command failed + class Error < StandardError + # @param details [#to_s] + def initialize(details) + super "Systemctl command failed: #{details}" + end + end + include Yast::Logger CONTROL = "systemctl".freeze @@ -32,10 +33,10 @@ def execute(command) log.info("systemctl #{command}") command = SYSTEMCTL + command log.debug "Executing `systemctl` command: #{command}" - result = ::Timeout.timeout(TIMEOUT) { SCR.Execute(BASH_SCR_PATH, command) } + result = ::Timeout.timeout(TIMEOUT) { Yast::SCR.Execute(BASH_SCR_PATH, command) } OpenStruct.new(result.merge!(command: command)) rescue ::Timeout::Error - raise SystemctlError, "Timeout #{TIMEOUT} seconds: #{command}" + raise Yast2::Systemctl::Error, "Timeout #{TIMEOUT} seconds: #{command}" end # @return [Array] like ["a.socket", "b.socket"] diff --git a/library/systemd/src/lib/yast2/systemd.rb b/library/systemd/src/lib/yast2/systemd.rb new file mode 100644 index 000000000..4730a464d --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd.rb @@ -0,0 +1,7 @@ +require "yast2/systemd/unit" +require "yast2/systemd/unit_prop_map" +require "yast2/systemd/unit_properties" +require "yast2/systemd/unit_installation_properties" +require "yast2/systemd/target" +require "yast2/systemd/service" +require "yast2/systemd/socket" diff --git a/library/systemd/src/lib/yast2/systemd/service.rb b/library/systemd/src/lib/yast2/systemd/service.rb new file mode 100644 index 000000000..e09a84ba4 --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/service.rb @@ -0,0 +1,221 @@ +require "yast" +require "yast2/systemd/unit" +require "yast2/systemd/unit_prop_map" +require "yast2/systemd/socket" + +module Yast2 + module Systemd + # Represent a missing service + class ServiceNotFound < StandardError + def initialize(service_name) + super "Service unit '#{service_name}' not found" + end + end + + # API to manage a systemd.service unit + # + # @example How to use it in other yast libraries + # require 'yast' + # require 'yast2/systemd/service' + # + # ## Get a service unit by its name + # ## If the service unit can't be found, you'll get nil + # + # service = Yast2::Systemd::Service.find('sshd') # service unit object + # + # # or using the full unit id 'sshd.service' + # + # service = Yast2::Systemd::Service.find('sshd.service') + # + # ## If you can't handle any nil at the place of calling, + # ## use the finder with exclamation mark; + # ## Systemd::ServiceNotFound exception will be raised + # + # service = Yast2::Systemd::Service.find!('IcanHasMoar') # Systemd::ServiceNotFound: Service unit 'IcanHasMoar' not found + # + # ## Get basic unit properties + # + # service.unit_name # 'sshd' + # service.unit_type # 'service' + # service.id # 'sshd.service' + # service.description # 'OpenSSH Daemon' + # service.path # '/usr/lib/systemd/system/sshd.service' + # service.loaded? # true if it's loaded, false otherwise + # service.running? # true if it's active and running + # service.enabled? # true if enabled, false otherwise + # service.disabled? # true if disabled, false otherwise + # service.status # the same text output you get with `systemctl status sshd.service` + # service.show # equivalent of calling `systemctl show sshd.service` + # + # ## Service unit file commands + # + # # Unit file commands do modifications on the service unit. Calling them triggers + # # service properties reloading. If a command fails, the error message is available + # # through the method #error as a string. + # + # service.start # true if unit has been activated successfully + # service.stop # true if unit has been deactivated successfully + # service.enable # true if unit has been enabled successfully + # service.disable # true if unit has been disabled successfully + # service.error # error string available if some of the actions above fails + # + # ## Extended service properties + # + # # In case you need more details about the service unit than the default ones, + # # you can extend the parameters for .find method. Those properties are + # # then available on the service unit object under the #properties instance method. + # # An extended property is always a string, you must convert it manually, + # # no automatical casting is done by yast. + # # To get an overview of available service properties, try e.g., `systemctl show sshd.service` + # + # service = Yast2::Systemd::Service.find('sshd', :type=>'Type') + # service.properties.type # 'simple' + class Service < Unit + Yast.import "Stage" + include Yast::Logger + + UNIT_SUFFIX = ".service".freeze + + class << self + # @param service_name [String] "foo" or "foo.service" + # @param propmap [Yast2::Systemd::UnitPropMap] + # @return [Service,nil] `nil` if not found + def find(service_name, propmap = UnitPropMap.new) + service = build(service_name, propmap) + return nil if service.properties.not_found? + service + end + + # @param service_name [String] "foo" or "foo.service" + # @param propmap [Yast2::Systemd::UnitPropMap] + # @return [Service] + # @raise [Systemd::ServiceNotFound] + def find!(service_name, propmap = UnitPropMap.new) + find(service_name, propmap) || raise(Systemd::ServiceNotFound, service_name) + end + + # @param service_names [Array] "foo" or "foo.service" + # @param propmap [Yast2::Systemd::UnitPropMap] + # @return [Array] `nil` if a service is not found, + # [] if this helper cannot be used: + # either we're in the inst-sys without systemctl, + # or it has returned fewer services than requested + # (and we cannot match them up) + def find_many_at_once(service_names, propmap = UnitPropMap.new) + return [] if Yast::Stage.initial + + snames = service_names.map { |n| n + UNIT_SUFFIX unless n.end_with?(UNIT_SUFFIX) } + snames_s = snames.join(" ") + pnames_s = UnitPropMap::DEFAULT.merge(propmap).values.join(",") + out = Systemctl.execute("show --property=#{pnames_s} #{snames_s}") + log.error "returned #{out.exit}, #{out.stderr}" unless out.exit.zero? && out.stderr.empty? + property_texts = out.stdout.split("\n\n") + return [] unless snames.size == property_texts.size + + snames.zip(property_texts).each_with_object([]) do |(name, property_text), memo| + service = new(name, propmap, property_text) + memo << service unless service.not_found? + end + end + + # @param service_names [Array] "foo" or "foo.service" + # @param propmap [Yast2::Systemd::UnitPropMap] + # @return [Array] `nil` if not found + def find_many(service_names, propmap = UnitPropMap.new) + services = find_many_at_once(service_names, propmap) + return services unless services.empty? + + log.info "Retrying one by one" + service_names.map { |n| find(n, propmap) } + end + + # @param propmap [Yast2::Systemd::UnitPropMap] + # @return [Array] + def all(propmap = UnitPropMap.new) + Systemctl.service_units.map { |s| new(s, propmap) } + end + + # Instantiate a Systemd::Service object based on the given name + # + # Use with caution as the service might exist or not. If you need to + # react when the service does not exist, use Systemd::Service.find. + # + # @param service_name [String] "foo" or "foo.service" + # @param propmap [Yast2::Systemd::UnitPropMap] + # @return [Service] System service with the given name + def build(service_name, propmap = UnitPropMap.new) + service_name += UNIT_SUFFIX unless service_name.end_with?(UNIT_SUFFIX) + new(service_name, propmap) + end + end + + private_class_method :find_many_at_once + + # Available only on installation system + START_SERVICE_INSTSYS_COMMAND = "/bin/service_start".freeze + + # @return [String] + def pid + properties.pid + end + + def running? + properties.running? + end + + def static? + properties.static? + end + + def start + command = "#{START_SERVICE_INSTSYS_COMMAND} #{unit_name}" + installation_system? ? run_instsys_command(command) : super + end + + def stop + command = "#{START_SERVICE_INSTSYS_COMMAND} --stop #{unit_name}" + installation_system? ? run_instsys_command(command) : super + end + + def restart + # Delegate to Systemd::Unit#restart if not within installation + return super unless installation_system? + + stop + sleep(1) + start + end + + # Returns socket associated with service or nil if there is no such socket + # + # @return [Yast2::Systemd::Socket,nil] + # @see Yast2::Systemd::Socket.for_service + def socket + @socket ||= Socket.for_service(name) + end + + # Determines whether the service has an associated socket + # + # @return [Boolean] true if an associated socket exists; false otherwise. + def socket? + !socket.nil? + end + + private + + def installation_system? + File.exist?(START_SERVICE_INSTSYS_COMMAND) + end + + def run_instsys_command(command) + log.info("Running command '#{command}'") + error.clear + result = OpenStruct.new( + Yast::SCR.Execute(Yast::Path.new(".target.bash_output"), command) + ) + error << result.stderr + result.exit.zero? + end + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd/socket.rb b/library/systemd/src/lib/yast2/systemd/socket.rb new file mode 100644 index 000000000..388bdd4e6 --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/socket.rb @@ -0,0 +1,109 @@ +require "yast2/systemd/unit" +require "yast2/systemd/socket_finder" + +module Yast2 + module Systemd + # Respresent missed socket + class SocketNotFound < StandardError + def initialize(socket_name) + super "Socket unit '#{socket_name}' not found" + end + end + + # API to manage a systemd.socket unit + # + # @example How to use it in other yast libraries + # + # require 'yast' + # require 'yast2/systemd/socket' + # + # ## Get a socket unit by its name + # ## If the socket unit can't be found, you'll get a nil object + # + # socket = Yast2::Systemd::Socket.find('iscsid') # socket unit object + # + # ## If you can't handle any nil at the place of calling, + # ## use the finder with exclamation mark; + # ## Systemd::SocketNotFound exception will be raised + # + # socket = Yast2::Systemd::Socket.find!('IcanHasCheez') # Systemd::SocketNotFound: Socket unit 'IcanHasCheez' not found + # + # ## Get basic unit properties + # + # socket.unit_name # 'iscsid' + # socket.unit_type # 'socket' + # socket.id # 'iscsid.socket' + # socket.path # '/usr/lib/systemd/system/iscsid.socket' => unit file path + # socket.loaded? # true if it's loaded, false otherwise + # socket.listening? # true if it's listening, false otherwise + # socket.enabled? # true if enabled, false otherwise + # socket.disabled? # true if disabled, false otherwise + # socket.status # the same string output you get with `systemctl status iscsid.socket` + # socket.show # equivalent of calling `systemctl show iscsid.socket` + # + # ## Socket unit file commands + # + # # Unit file commands do modifications on the socket unit. Calling them triggers + # # socket properties reloading. In case a command fails, the error messages are available + # # through the method #errors as a string. + # + # socket.start # true if unit has been activated successfully + # socket.stop # true if unit has been deactivated successfully + # socket.enable # true if unit has been enabled successfully + # socket.disable # true if unit has been disabled successfully + # socket.errors # error string available if some of the actions above fails + # + # ## Extended socket properties + # + # # In case you need more details about the socket unit than the default ones, + # # you can extend the paramters when getting the socket. Those properties are + # # then available under the #properties instance method. To get an overview of + # # available socket properties, try e.g., `systemctl show iscsid.socket` + # + # socket = Yast2::Systemd::Socket.find('iscsid', :can_start=>'CanStart', :triggers=>'Triggers') + # socket.properties.can_start # 'yes' + # socket.properties.triggers # 'iscsid.service' + class Socket < Unit + UNIT_SUFFIX = ".socket".freeze + + class << self + # @param propmap [Yast2::Systemd::UnitPropMap] + def find(socket_name, propmap = {}) + socket_name += UNIT_SUFFIX unless socket_name.end_with?(UNIT_SUFFIX) + socket = new(socket_name, propmap) + return nil if socket.properties.not_found? + socket + end + + # @param propmap [Yast2::Systemd::UnitPropMap] + def find!(socket_name, propmap = {}) + find(socket_name, propmap) || raise(Systemd::SocketNotFound, socket_name) + end + + # @param propmap [Yast2::Systemd::UnitPropMap] + def all(propmap = {}) + sockets = Systemctl.socket_units.map { |s| new(s, propmap) } + sockets.select { |s| s.properties.supported? } + end + + # Returns the socket for a given service + # + # @param service_name [String] Service name (without the `.service` extension) + # @return [Yast2::Systemd::Socket, nil] + def for_service(service_name) + @socket_finder ||= Yast2::Systemd::SocketFinder.new + @socket_finder.for_service(service_name) + end + + # Resets the cache for socket finder + def reset + @socket_finder = nil + end + end + + def listening? + properties.sub_state == "listening" + end + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd/socket_finder.rb b/library/systemd/src/lib/yast2/systemd/socket_finder.rb new file mode 100644 index 000000000..ca2d69e83 --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/socket_finder.rb @@ -0,0 +1,107 @@ +# encoding: utf-8 + +# Copyright (c) [2018] 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 "yast" +require "yast2/execute" +require "yast2/systemctl" + +Yast.import "Stage" + +module Yast2 + module Systemd + # This class is responsible for finding out which sockets trigger a given service. + # + # When systemd is working properly, the relationship between services and sockets is cached in + # order to reduce the amount of calls to `systemctl show`. However, during the installation, where + # systemd is not fully operational, this class just tries to find a socket named after the + # service. + # + # @example Finding a socket + # finder = Yast2::SystemdSocketFinder.new + # finder.for_service("cups").class # => Yast2::Systemd::Socket + class SocketFinder + # Returns the socket for a given service + # + # @param service_name [String] Service name (without the `.service` extension) + # @return [Yast2::SystemdSocket, nil] + def for_service(service_name) + socket_name = socket_name_for(service_name) + return nil unless socket_name + Yast2::Systemd::Socket.find(socket_name) + end + + private + + # Return the socket's name for a given service + # + # @note On 1st stage it returns just the same name. + # + # @return [String,nil] + def socket_name_for(service_name) + return service_name if Yast::Stage.initial + sockets_map[service_name] + end + + # Builds a map between services and sockets + # + # @note When more than one socket triggers the service, the last one will be used. + # + # @return [Hash] Sockets indexed by the name of the service they trigger + def sockets_map + return @sockets_map if @sockets_map + result = Yast2::Systemctl.execute(show_triggers_cmd) + return {} unless result.exit.zero? + lines = result.stdout.lines.map(&:chomp) + @sockets_map = lines.each_slice(3).each_with_object({}) do |(id_str, triggers_str, _), memo| + id = id_str[/Id=(\w+).socket/, 1] + triggers = triggers_str[/Triggers=(\w+).service/, 1] + memo[triggers] = id if triggers && id + end + end + + # @return [String] systemctl command to get services and their triggers + SHOW_TRIGGERS_CMD = "show --property Id,Triggers %s".freeze + + # Returns the systemctl show command to get sockets details + # + # @note The list is alphabetically ordered. + # + # @return [String] systemctl show command + def show_triggers_cmd + format(SHOW_TRIGGERS_CMD, unit_names: unit_names.sort.join(" ")) + end + + # Returns the names of the socket units + # + # @return [Array] Socket unit names + def unit_names + output = Yast::Execute.on_target( + "/usr/bin/systemctl", "list-unit-files", "--type", "socket", + stdout: :capture + ) + output.lines.each_with_object([]) do |name, memo| + socket_name = name[/\A(\w+.socket)/, 1] + memo << socket_name if socket_name + end + end + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd/target.rb b/library/systemd/src/lib/yast2/systemd/target.rb new file mode 100644 index 000000000..3602401e3 --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/target.rb @@ -0,0 +1,121 @@ +require "yast" +require "yast2/systemd/unit" + +module Yast2 + module Systemd + # Represents that the given service does not exists + class TargetNotFound < StandardError + def initialize(target_name) + super "Target unit '#{target_name}' not found" + end + end + + # API to manage a systemd.target unit + # + # @example How to find a custom systemd target + # + # require 'yast' + # require 'yast2/systemd/target' + # + # # This will return either a target object or nil + # target = Yast2::Systemd::Target.find('graphical') + # + # # This returns target object or raises exception Systemd::TargetNotFound + # target = Yast2::Systemd::Target.find!('whatever') + # + # # This returns collection of all available targets + # Yast2::Systemd::Target.all + # + # @example How to find the current default target + # + # target = Yast2::Systemd::Target.get_default + # target.unit_name # name of the default target + # target.allow_isolate? # should return true + # + # @example Set the default target + # + # Yast2::Systemd::Target.set_default('multi-user') # returns true if success + # + # # Or if we have already an target object, use this for default target + # target = Yast2::Systemd::Target.find('graphical') + # target.allow_isolate? # must be true to set default target + # target.set_default # returns true if success + class Target < Unit + include Yast::Logger + + UNIT_SUFFIX = ".target".freeze + DEFAULT_TARGET = "default.target".freeze + # @return [Yast2::Systemd::UnitPropMap] + PROPMAP = { allow_isolate: "AllowIsolate" }.freeze + + # Disable unsupported methods for target units + undef_method :start, :stop, :enable, :disable, :restart + + class << self + # @param propmap [Yast2::Systemd::UnitPropMap] + def find(target_name, propmap = {}) + target_name += UNIT_SUFFIX unless target_name.end_with?(UNIT_SUFFIX) + target = new(target_name, PROPMAP.merge(propmap)) + + if target.properties.not_found? + log.error "Target #{target_name} not found: #{target.properties.inspect}" + return nil + end + + target + end + + # @param propmap [Yast2::Systemd::UnitPropMap] + def find!(target_name, propmap = {}) + find(target_name, propmap) || raise(Systemd::TargetNotFound, target_name) + end + + # @param propmap [Yast2::Systemd::UnitPropMap] + def all(propmap = {}) + targets = Systemctl.target_units.map do |target_unit_name| + find(target_unit_name, propmap) + end + targets.compact + end + + def get_default # rubocop:disable Style/AccessorMethodName + result = Systemctl.execute("get-default") + raise(SystemctlError, result) unless result.exit.zero? + + find(result.stdout.strip) + end + + def set_default(target) # rubocop:disable Style/AccessorMethodName + target_unit = target.is_a?(Systemd::Target) ? target : find(target) + + unless target_unit + log.error "Cannot find target #{target.inspect}" + return false + end + + target_unit.set_default + end + end + + def allow_isolate? + # We cannot find out a target properties from /mnt in inst-sys + # systemctl doesn't return any properties in chroot + # See bnc#889323 + ["yes", nil].include?(properties.allow_isolate) + end + + def set_default + unless allow_isolate? + log.error "Cannot set #{id.inspect} as default target: Cannot be isolated (#{properties.allow_isolate})" + return false + end + + # Constructing a fallback target ID if we can't get it from systemctl + target_name = id ? id : "#{name}.target" + + result = Systemctl.execute("set-default --force #{target_name}") + result.exit.zero? + end + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd/unit.rb b/library/systemd/src/lib/yast2/systemd/unit.rb new file mode 100644 index 000000000..8e8d53cd4 --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/unit.rb @@ -0,0 +1,200 @@ +require "yast2/systemctl" +require "yast2/systemd/unit_prop_map" +require "yast2/systemd/unit_properties" +require "yast2/systemd/unit_installation_properties" + +require "ostruct" + +module Yast2 + module Systemd + ### + # Use this class always as a parent class for implementing various systemd units. + # Do not use it directly for ad-hoc implementation of systemd units. + # + # @example Create a systemd service unit + # + # class Service < Yast2::Systemd::Unit + # SUFFIX = ".service" + # PROPMAP = { + # before: "Before" + # } + # + # def initialize service_name, propmap={} + # service_name += SUFFIX unless service_name.end_with?(SUFFIX) + # super(service_name, PROPMAP.merge(propmap)) + # end + # + # def before + # properties.before.split + # end + # end + # + # service = Service.new('sshd') + # + # service.before # ['shutdown.target', 'multi-user.target'] + # + class Unit + Yast.import "Stage" + include Yast::Logger + + SUPPORTED_TYPES = %w(service socket target).freeze + + # with ruby 2.4 delegating ostruct with Forwardable start to write warning + # so define it manually (bsc#1049433) + FORWARDED_METHODS = [ + :id, + :path, + :description, + :active?, + :enabled?, + :loaded?, + :active_state, + :sub_state, + :can_reload?, + :not_found? + ].freeze + + private_constant :FORWARDED_METHODS + + FORWARDED_METHODS.each { |m| define_method(m) { properties.public_send(m) } } + + # @return [String] eg. "apache2" + # (the canonical one, may be different from unit_name) + attr_reader :name + # @return [String] eg. "apache2" + attr_reader :unit_name + # @return [String] eg. "service" + attr_reader :unit_type + # @return [Yast2::Systemd::UnitPropMap] + attr_reader :propmap + # @return [String] + # eg "Failed to get properties: Unit name apache2@.service is not valid." + attr_reader :error + # @return [Yast2::Systemd::UnitProperties] + attr_reader :properties + + # @param full_unit_name [String] eg "foo.service" + # @param propmap [Yast2::Systemd::UnitPropMap] + # @param property_text [String,nil] + # if provided, use it instead of calling `systemctl show` + def initialize(full_unit_name, propmap = UnitPropMap.new, property_text = nil) + @unit_name, dot, @unit_type = full_unit_name.rpartition(".") + raise "Missing unit type suffix" if dot.empty? + + log.warn "Unsupported unit type '#{unit_type}'" unless SUPPORTED_TYPES.include?(unit_type) + @propmap = propmap.merge!(UnitPropMap::DEFAULT) + + @properties = show(property_text) + @error = properties.error + # Id is not present when the unit name is not valid + @name = id.to_s.split(".").first || unit_name + end + + # @raise [Yast::SystemctlError] if 'systemctl show' cannot be executed + def refresh! + @properties = show + @error = properties.error + properties + end + + # Run 'systemctl show' and parse the unit properties + # + # @raise [Yast::SystemctlError] if 'systemctl show' cannot be executed + # + # @param property_text [String,nil] if provided, use it instead of calling `systemctl show` + # @return [Yast2::Systemd::UnitProperties] + def show(property_text = nil) + # Using different handler during first stage (installation, update, ...) + if Yast::Stage.initial + UnitInstallationProperties.new(self) + else + UnitProperties.new(self, property_text) + end + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [String] + def status + command("status", options: "2>&1").stdout + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly started + def start + run_command! { command("start") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly stopped + def stop + run_command! { command("stop") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly enabled + def enable + run_command! { command("enable") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly disabled + def disable + run_command! { command("disable") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly restarted + def restart + run_command! { command("restart") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the service was correctly restarted + def try_restart + run_command! { command("try-restart") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly reloaded + def reload + run_command! { command("reload") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly reloaded or restarted + def reload_or_restart + run_command! { command("reload-or-restart") } + end + + # @raise [Yast::SystemctlError] if the command cannot be executed + # @return [Boolean] true if the unit was correctly reloaded or restarted + def reload_or_try_restart + run_command! { command("reload-or-try-restart") } + end + + # Runs a command in the underlying system + # + # @raise [Yast::SystemctlError] if the command cannot be executed + # + # @param command_name [String] + # @return [#command, #stdout, #stderr, #exit] + def command(command_name, options = {}) + command = "#{command_name} #{unit_name}.#{unit_type} #{options[:options]}" + Systemctl.execute(command) + end + + private + + # Run a command, pass its stderr to {#error}, {#refresh!}. + # @yieldreturn [#command,#stdout,#stderr,#exit] + # @return [Boolean] success? (exit was zero) + def run_command! + error.clear + command_result = yield + error << command_result.stderr + refresh! + command_result.exit.zero? + end + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd/unit_installation_properties.rb b/library/systemd/src/lib/yast2/systemd/unit_installation_properties.rb new file mode 100644 index 000000000..a26ec517a --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/unit_installation_properties.rb @@ -0,0 +1,62 @@ +require "yast" + +module Yast2 + module Systemd + # A replacement for {Yast2::Systemd::UnitPropertie} during installation + # + # Systemd `show` command (systemctl show) is not available during + # installation and will return error "Running in chroot, ignoring request." + # Therefore, we must avoid calling it in the installation workflow, reason + # why exist this class that helps to keep the API partially consistent. + # + # It has two goals: + # + # 1. Checks for existence of the unit based on the stderr from the + # command `systemctl is-enabled` + # 2. Retrieves the status enabled|disabled which is needed in the + # installation system. + # + # There are currently only 3 commands available for systemd in + # inst-sys/chroot: `systemctl enable|disable|is-enabled`. The rest will + # return the error message mentioned above. + # + # @note Once the inst-sys has running dbus/systemd, this class definition + # can be removed together with the condition for Stage.initial in the + # {Yast2::Systemd::Unit#show} + class UnitInstallationProperties < OpenStruct + include Yast::Logger + + def initialize(systemd_unit) + super() + self[:systemd_unit] = systemd_unit + self[:status] = read_status + self[:raw] = status.stdout + self[:error] = status.stderr + self[:exit] = status.exit + self[:enabled?] = status.exit.zero? + self[:not_found?] = service_missing? + end + + private + + def read_status + systemd_unit.command("is-enabled") + end + + # Analyze the exit code and stdout of the command `systemctl is-enabled + # service_name` + # + # @see https://www.freedesktop.org/software/systemd/man/systemctl.html#is-enabled%20UNIT%E2%80%A6 + # + # @return [Boolean] true if service does not exist; false otherwise + def service_missing? + # the service exists and it's enabled + return false if status.exit.zero? + # the service exists and it's disabled + return false if status.exit.nonzero? && status.stdout.match(/disabled|masked|linked/) + # for all other cases the service does not exist + true + end + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd/unit_prop_map.rb b/library/systemd/src/lib/yast2/systemd/unit_prop_map.rb new file mode 100644 index 000000000..4447e28c5 --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/unit_prop_map.rb @@ -0,0 +1,23 @@ +module Yast2 + module Systemd + # A Unit Property Map is a plain Hash(Symbol => String). + # + # It + # 1. enumerates the properties we're interested in + # 2. maps their Ruby names (snake_case) to systemd names (CamelCase) + class UnitPropMap < Hash + # @return [Yast2::Systemd::UniPropMap] + DEFAULT = UnitPropMap[{ + id: "Id", + pid: "MainPID", + description: "Description", + load_state: "LoadState", + active_state: "ActiveState", + sub_state: "SubState", + unit_file_state: "UnitFileState", + path: "FragmentPath", + can_reload: "CanReload" + }].freeze + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd/unit_properties.rb b/library/systemd/src/lib/yast2/systemd/unit_properties.rb new file mode 100644 index 000000000..d3b9def68 --- /dev/null +++ b/library/systemd/src/lib/yast2/systemd/unit_properties.rb @@ -0,0 +1,101 @@ +require "yast" + +module Yast2 + module Systemd + # Structure holding properties of systemd unit + class UnitProperties < OpenStruct + include Yast::Logger + + SUPPORTED_STATES = %w(enabled disabled).freeze + + # Values of {#active_state} fow which we consider a unit "active". + # + # systemctl.c:check_unit_active uses (active, reloading) + # For bsc#884756 we also consider "activating" to be active. + # (The remaining states are "deactivating", "inactive", "failed".) + # + # Yes, depending on systemd states that are NOT covered by their + # interface stability promise is fragile. + # But: 10 to 50ms per call of systemctl is-active, times 100 to 300 services + # (depending on hardware and software installed, VM or not) + # is a 1 to 15 second delay (bsc#1045658). + # That is why we try hard to avoid many systemctl calls. + ACTIVE_STATES = ["active", "activating", "reloading"].freeze + + # @param systemd_unit [Yast2::Systemd::Unit] + # @param property_text [String,nil] if provided, use it instead of calling `systemctl show` + def initialize(systemd_unit, property_text) + super() + self[:systemd_unit] = systemd_unit + if property_text.nil? + raw_output = load_systemd_properties + self[:raw] = raw_output.stdout + self[:error] = raw_output.stderr + self[:exit] = raw_output.exit + else + self[:raw] = property_text + self[:error] = "" + self[:exit] = 0 + end + + if !exit.zero? || !error.empty? + message = "Failed to get properties for unit '#{systemd_unit.unit_name}' ; " + message << "Command `#{raw_output.command}` returned error: #{error}" + log.error(message) + self[:not_found?] = true + return + end + + extract_properties + self[:active?] = ACTIVE_STATES.include?(active_state) + self[:running?] = sub_state == "running" + self[:loaded?] = load_state == "loaded" + self[:not_found?] = load_state == "not-found" + self[:static?] = load_state == "static" + self[:enabled?] = read_enabled_state + self[:supported?] = SUPPORTED_STATES.include?(unit_file_state) + self[:can_reload?] = can_reload == "yes" + end + + private + + # Check the value of #unit_file_state; its value mirrors UnitFileState dbus property + # @return [Boolean] True if enabled, False if not + def read_enabled_state + # If UnitFileState property is missing (due to e.g. legacy sysvinit service) or + # has an unknown entry (e.g. "bad") we must use a different way how to get the + # real status of the service. + if SUPPORTED_STATES.include?(unit_file_state) + state_name_enabled?(unit_file_state) + else + # Check for exit code of `systemctl is-enabled systemd_unit.name` ; additionally + # test the stdout of the command for valid values when the service is enabled + # https://www.freedesktop.org/software/systemd/man/systemctl.html#is-enabled%20UNIT%E2%80%A6 + status = systemd_unit.command("is-enabled") + status.exit.zero? && state_name_enabled?(status.stdout) + end + end + + # Systemd service unit can have various states like enabled, enabled-runtime, + # linked, linked-runtime, masked, masked-runtime, static, disabled, invalid. + # We test for the return value 'enabled' and 'enabled-runtime' to consider + # a service as enabled. + # @return [Boolean] True if enabled, False if not + def state_name_enabled?(state) + ["enabled", "enabled-runtime"].include?(state.strip) + end + + def extract_properties + systemd_unit.propmap.each do |name, property| + self[name] = raw[/#{property}=(.+)/, 1] + end + end + + def load_systemd_properties + names = systemd_unit.propmap.values + opts = names.map { |property_name| " --property=#{property_name} " } + systemd_unit.command("show", options: opts.join) + end + end + end +end diff --git a/library/systemd/src/lib/yast2/systemd_socket_finder.rb b/library/systemd/src/lib/yast2/systemd_socket_finder.rb deleted file mode 100644 index 7e714fdae..000000000 --- a/library/systemd/src/lib/yast2/systemd_socket_finder.rb +++ /dev/null @@ -1,105 +0,0 @@ -# encoding: utf-8 - -# Copyright (c) [2018] 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 "yast" -require "yast2/execute" - -Yast.import "Systemctl" -Yast.import "Stage" - -module Yast2 - # This class is responsible for finding out which sockets trigger a given service. - # - # When systemd is working properly, the relationship between services and sockets is cached in - # order to reduce the amount of calls to `systemctl show`. However, during the installation, where - # systemd is not fully operational, this class just tries to find a socket named after the - # service. - # - # @example Finding a socket - # finder = Yast2::SystemdSocketFinder.new - # finder.for_service("cups").class # => Yast::SystemdSocketClass::Socket - class SystemdSocketFinder - # Returns the socket for a given service - # - # @param service_name [String] Service name (without the `.service` extension) - # @return [Yast2::SystemdSocketClass::Socket,nil] - def for_service(service_name) - socket_name = socket_name_for(service_name) - return nil unless socket_name - Yast::SystemdSocket.find(socket_name) - end - - private - - # Return the socket's name for a given service - # - # @note On 1st stage it returns just the same name. - # - # @return [String,nil] - def socket_name_for(service_name) - return service_name if Yast::Stage.initial - sockets_map[service_name] - end - - # Builds a map between services and sockets - # - # @note When more than one socket triggers the service, the last one will be used. - # - # @return [Hash] Sockets indexed by the name of the service they trigger - def sockets_map - return @sockets_map if @sockets_map - result = Yast::Systemctl.execute(show_triggers_cmd) - return {} unless result.exit.zero? - lines = result.stdout.lines.map(&:chomp) - @sockets_map = lines.each_slice(3).each_with_object({}) do |(id_str, triggers_str, _), memo| - id = id_str[/Id=(\w+).socket/, 1] - triggers = triggers_str[/Triggers=(\w+).service/, 1] - memo[triggers] = id if triggers && id - end - end - - # @return [String] systemctl command to get services and their triggers - SHOW_TRIGGERS_CMD = "show --property Id,Triggers %s".freeze - - # Returns the systemctl show command to get sockets details - # - # @note The list is alphabetically ordered. - # - # @return [String] systemctl show command - def show_triggers_cmd - format(SHOW_TRIGGERS_CMD, unit_names: unit_names.sort.join(" ")) - end - - # Returns the names of the socket units - # - # @return [Array] Socket unit names - def unit_names - output = Yast::Execute.on_target( - "/usr/bin/systemctl", "list-unit-files", "--type", "socket", - stdout: :capture - ) - output.lines.each_with_object([]) do |name, memo| - socket_name = name[/\A(\w+.socket)/, 1] - memo << socket_name if socket_name - end - end - end -end diff --git a/library/systemd/src/lib/yast2/systemd_unit.rb b/library/systemd/src/lib/yast2/systemd_unit.rb deleted file mode 100644 index 68668b574..000000000 --- a/library/systemd/src/lib/yast2/systemd_unit.rb +++ /dev/null @@ -1,354 +0,0 @@ -require "yast2/systemctl" - -require "ostruct" - -module Yast - ### - # Use this class always as a parent class for implementing various systemd units. - # Do not use it directly for ad-hoc implementation of systemd units. - # - # @example Create a systemd service unit - # - # class Service < Yast::SystemdUnit - # SUFFIX = ".service" - # PROPMAP = { - # before: "Before" - # } - # - # def initialize service_name, propmap={} - # service_name += SUFFIX unless service_name.end_with?(SUFFIX) - # super(service_name, PROPMAP.merge(propmap)) - # end - # - # def before - # properties.before.split - # end - # end - # - # service = Service.new('sshd') - # - # service.before # ['shutdown.target', 'multi-user.target'] - # - class SystemdUnit - Yast.import "Stage" - include Yast::Logger - - SUPPORTED_TYPES = %w(service socket target).freeze - SUPPORTED_STATES = %w(enabled disabled).freeze - - # Values of {#active_state} fow which we consider a unit "active". - # - # systemctl.c:check_unit_active uses (active, reloading) - # For bsc#884756 we also consider "activating" to be active. - # (The remaining states are "deactivating", "inactive", "failed".) - # - # Yes, depending on systemd states that are NOT covered by their - # interface stability promise is fragile. - # But: 10 to 50ms per call of systemctl is-active, times 100 to 300 services - # (depending on hardware and software installed, VM or not) - # is a 1 to 15 second delay (bsc#1045658). - # That is why we try hard to avoid many systemctl calls. - ACTIVE_STATES = ["active", "activating", "reloading"].freeze - - # A Property Map is a plain Hash(Symbol => String). - # It - # 1. enumerates the properties we're interested in - # 2. maps their Ruby names (snake_case) to systemd names (CamelCase) - class PropMap < Hash - end - - # @return [PropMap] - DEFAULT_PROPMAP = { - id: "Id", - pid: "MainPID", - description: "Description", - load_state: "LoadState", - active_state: "ActiveState", - sub_state: "SubState", - unit_file_state: "UnitFileState", - path: "FragmentPath", - can_reload: "CanReload" - }.freeze - - # with ruby 2.4 delegating ostruct with Forwardable start to write warning - # so define it manually (bsc#1049433) - FORWARDED_METHODS = [ - :id, - :path, - :description, - :active?, - :enabled?, - :loaded?, - :active_state, - :sub_state, - :can_reload?, - :not_found? - ].freeze - - private_constant :FORWARDED_METHODS - - FORWARDED_METHODS.each { |m| define_method(m) { properties.public_send(m) } } - - # @return [String] eg. "apache2" - # (the canonical one, may be different from unit_name) - attr_reader :name - # @return [String] eg. "apache2" - attr_reader :unit_name - # @return [String] eg. "service" - attr_reader :unit_type - # @return [PropMap] - attr_reader :propmap - # @return [String] - # eg "Failed to get properties: Unit name apache2@.service is not valid." - attr_reader :error - # @return [Properties] - attr_reader :properties - - # @param full_unit_name [String] eg "foo.service" - # @param propmap [PropMap] - # @param property_text [String,nil] - # if provided, use it instead of calling `systemctl show` - def initialize(full_unit_name, propmap = {}, property_text = nil) - @unit_name, dot, @unit_type = full_unit_name.rpartition(".") - raise "Missing unit type suffix" if dot.empty? - - log.warn "Unsupported unit type '#{unit_type}'" unless SUPPORTED_TYPES.include?(unit_type) - @propmap = propmap.merge!(DEFAULT_PROPMAP) - - @properties = show(property_text) - @error = properties.error - # Id is not present when the unit name is not valid - @name = id.to_s.split(".").first || unit_name - end - - # @raise [Yast::SystemctlError] if 'systemctl show' cannot be executed - def refresh! - @properties = show - @error = properties.error - properties - end - - # Run 'systemctl show' and parse the unit properties - # - # @raise [Yast::SystemctlError] if 'systemctl show' cannot be executed - # - # @param property_text [String,nil] if provided, use it instead of calling `systemctl show` - # @return [Properties] - def show(property_text = nil) - # Using different handler during first stage (installation, update, ...) - Stage.initial ? InstallationProperties.new(self) : Properties.new(self, property_text) - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [String] - def status - command("status", options: "2>&1").stdout - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly started - def start - run_command! { command("start") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly stopped - def stop - run_command! { command("stop") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly enabled - def enable - run_command! { command("enable") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly disabled - def disable - run_command! { command("disable") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly restarted - def restart - run_command! { command("restart") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the service was correctly restarted - def try_restart - run_command! { command("try-restart") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly reloaded - def reload - run_command! { command("reload") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly reloaded or restarted - def reload_or_restart - run_command! { command("reload-or-restart") } - end - - # @raise [Yast::SystemctlError] if the command cannot be executed - # @return [Boolean] true if the unit was correctly reloaded or restarted - def reload_or_try_restart - run_command! { command("reload-or-try-restart") } - end - - # Runs a command in the underlying system - # - # @raise [Yast::SystemctlError] if the command cannot be executed - # - # @param command_name [String] - # @return [#command, #stdout, #stderr, #exit] - def command(command_name, options = {}) - command = "#{command_name} #{unit_name}.#{unit_type} #{options[:options]}" - Systemctl.execute(command) - end - - private - - # Run a command, pass its stderr to {#error}, {#refresh!}. - # @yieldreturn [#command,#stdout,#stderr,#exit] - # @return [Boolean] success? (exit was zero) - def run_command! - error.clear - command_result = yield - error << command_result.stderr - refresh! - command_result.exit.zero? - end - - # Structure holding properties of systemd unit - class Properties < OpenStruct - include Yast::Logger - - # @param systemd_unit [SystemdUnit] - # @param property_text [String,nil] - # if provided, use it instead of calling `systemctl show` - def initialize(systemd_unit, property_text) - super() - self[:systemd_unit] = systemd_unit - if property_text.nil? - raw_output = load_systemd_properties - self[:raw] = raw_output.stdout - self[:error] = raw_output.stderr - self[:exit] = raw_output.exit - else - self[:raw] = property_text - self[:error] = "" - self[:exit] = 0 - end - - if !exit.zero? || !error.empty? - message = "Failed to get properties for unit '#{systemd_unit.unit_name}' ; " - message << "Command `#{raw_output.command}` returned error: #{error}" - log.error(message) - self[:not_found?] = true - return - end - - extract_properties - self[:active?] = ACTIVE_STATES.include?(active_state) - self[:running?] = sub_state == "running" - self[:loaded?] = load_state == "loaded" - self[:not_found?] = load_state == "not-found" - self[:static?] = unit_file_state == "static" - self[:enabled?] = read_enabled_state - self[:supported?] = SUPPORTED_STATES.include?(unit_file_state) - self[:can_reload?] = can_reload == "yes" - end - - private - - # Check the value of #unit_file_state; its value mirrors UnitFileState dbus property - # @return [Boolean] True if enabled, False if not - def read_enabled_state - # If UnitFileState property is missing (due to e.g. legacy sysvinit service) or - # has an unknown entry (e.g. "bad") we must use a different way how to get the - # real status of the service. - if SUPPORTED_STATES.include?(unit_file_state) - state_name_enabled?(unit_file_state) - else - # Check for exit code of `systemctl is-enabled systemd_unit.name` ; additionally - # test the stdout of the command for valid values when the service is enabled - # http://www.freedesktop.org/software/systemd/man/systemctl.html#is-enabled%20NAME... - status = systemd_unit.command("is-enabled") - status.exit.zero? && state_name_enabled?(status.stdout) - end - end - - # Systemd service unit can have various states like enabled, enabled-runtime, - # linked, linked-runtime, masked, masked-runtime, static, disabled, invalid. - # We test for the return value 'enabled' and 'enabled-runtime' to consider - # a service as enabled. - # @return [Boolean] True if enabled, False if not - def state_name_enabled?(state) - ["enabled", "enabled-runtime"].include?(state.strip) - end - - def extract_properties - systemd_unit.propmap.each do |name, property| - self[name] = raw[/#{property}=(.+)/, 1] - end - end - - def load_systemd_properties - names = systemd_unit.propmap.values - opts = names.map { |property_name| " --property=#{property_name} " } - systemd_unit.command("show", options: opts.join) - end - end - - # systemd command `systemctl show` is not available during installation - # and will return error "Running in chroot, ignoring request." Therefore, we must - # avoid calling it in the installation workflow. To keep the API partially - # consistent, this class offers a replacement for the Properties above. - # - # It has two goals: - # 1. Checks for existence of the unit based on the stderr from the command - # `systemctl is-enabled` - # 2. Retrieves the status enabled|disabled which is needed in the installation - # system. There are currently only 3 commands available for systemd in - # inst-sys/chroot: `systemctl enable|disable|is-enabled`. The rest will return - # the error message mentioned above in this comment. - # - # Once the inst-sys has running dbus/systemd, this class definition can be removed - # together with the condition for Stage.initial in the SystemdUnit#show. - class InstallationProperties < OpenStruct - include Yast::Logger - - def initialize(systemd_unit) - super() - self[:systemd_unit] = systemd_unit - self[:status] = read_status - self[:raw] = status.stdout - self[:error] = status.stderr - self[:exit] = status.exit - self[:enabled?] = status.exit.zero? - self[:not_found?] = service_missing? - end - - private - - def read_status - systemd_unit.command("is-enabled") - end - - # Analyze the exit code and stdout of the command `systemctl is-enabled service_name` - # http://www.freedesktop.org/software/systemd/man/systemctl.html#is-enabled%20NAME... - def service_missing? - # the service exists and it's enabled - return false if status.exit.zero? - # the service exists and it's disabled - return false if status.exit.nonzero? && status.stdout.match(/disabled|masked|linked/) - # for all other cases the service does not exist - true - end - end - end -end diff --git a/library/systemd/src/modules/systemd_service.rb b/library/systemd/src/modules/systemd_service.rb deleted file mode 100644 index b6eda33ef..000000000 --- a/library/systemd/src/modules/systemd_service.rb +++ /dev/null @@ -1,220 +0,0 @@ -require "yast2/systemd_unit" - -Yast.import "SystemdSocket" - -module Yast - class SystemdServiceNotFound < StandardError - def initialize(service_name) - super "Service unit '#{service_name}' not found" - end - end - - # Systemd.service unit control API - # - # @example How to use it in other yast libraries - # require 'yast' - # Yast.import 'SystemdService' - # - # ## Get a service unit by its name - # ## If the service unit can't be found, you'll get nil - # - # service = Yast::SystemdService.find('sshd') # service unit object - # - # # or using the full unit id 'sshd.service' - # - # service = Yast::SystemdService.find('sshd.service') - # - # ## If you can't handle any nil at the place of calling, - # ## use the finder with exclamation mark; - # ## SystemdServiceNotFound exception will be raised - # - # service = Yast::SystemdService.find!('IcanHasMoar') # SystemdServiceNotFound: Service unit 'IcanHasMoar' not found - # - # ## Get basic unit properties - # - # service.unit_name # 'sshd' - # service.unit_type # 'service' - # service.id # 'sshd.service' - # service.description # 'OpenSSH Daemon' - # service.path # '/usr/lib/systemd/system/sshd.service' - # service.loaded? # true if it's loaded, false otherwise - # service.running? # true if it's active and running - # service.enabled? # true if enabled, false otherwise - # service.disabled? # true if disabled, false otherwise - # service.status # the same text output you get with `systemctl status sshd.service` - # service.show # equivalent of calling `systemctl show sshd.service` - # - # ## Service unit file commands - # - # # Unit file commands do modifications on the service unit. Calling them triggers - # # service properties reloading. If a command fails, the error message is available - # # through the method #error as a string. - # - # service.start # true if unit has been activated successfully - # service.stop # true if unit has been deactivated successfully - # service.enable # true if unit has been enabled successfully - # service.disable # true if unit has been disabled successfully - # service.error # error string available if some of the actions above fails - # - # ## Extended service properties - # - # # In case you need more details about the service unit than the default ones, - # # you can extend the parameters for .find method. Those properties are - # # then available on the service unit object under the #properties instance method. - # # An extended property is always a string, you must convert it manually, - # # no automatical casting is done by yast. - # # To get an overview of available service properties, try e.g., `systemctl show sshd.service` - # - # service = Yast::SystemdService.find('sshd', :type=>'Type') - # service.properties.type # 'simple' - class SystemdServiceClass < Module - Yast.import "Stage" - include Yast::Logger - - UNIT_SUFFIX = ".service".freeze - - # @param service_name [String] "foo" or "foo.service" - # @param propmap [SystemdUnit::PropMap] - # @return [Service,nil] `nil` if not found - def find(service_name, propmap = {}) - service = build(service_name, propmap) - return nil if service.properties.not_found? - service - end - - # @param service_name [String] "foo" or "foo.service" - # @param propmap [SystemdUnit::PropMap] - # @return [Service] - # @raise [SystemdServiceNotFound] - def find!(service_name, propmap = {}) - find(service_name, propmap) || raise(SystemdServiceNotFound, service_name) - end - - # @param service_names [Array] "foo" or "foo.service" - # @param propmap [SystemdUnit::PropMap] - # @return [Array] `nil` if a service is not found, - # [] if this helper cannot be used: - # either we're in the inst-sys without systemctl, - # or it has returned fewer services than requested - # (and we cannot match them up) - private def find_many_at_once(service_names, propmap = {}) - return [] if Stage.initial - - snames = service_names.map { |n| n + UNIT_SUFFIX unless n.end_with?(UNIT_SUFFIX) } - snames_s = snames.join(" ") - pnames_s = SystemdUnit::DEFAULT_PROPMAP.merge(propmap).values.join(",") - out = Systemctl.execute("show --property=#{pnames_s} #{snames_s}") - log.error "returned #{out.exit}, #{out.stderr}" unless out.exit.zero? && out.stderr.empty? - property_texts = out.stdout.split("\n\n") - return [] unless snames.size == property_texts.size - - snames.zip(property_texts).each_with_object([]) do |(name, property_text), memo| - service = Service.new(name, propmap, property_text) - memo << service unless service.not_found? - end - end - - # @param service_names [Array] "foo" or "foo.service" - # @param propmap [SystemdUnit::PropMap] - # @return [Array] `nil` if not found - def find_many(service_names, propmap = {}) - services = find_many_at_once(service_names, propmap) - return services unless services.empty? - - log.info "Retrying one by one" - service_names.map { |n| find(n, propmap) } - end - - # @param propmap [SystemdUnit::PropMap] - # @return [Array] - def all(propmap = {}) - Systemctl.service_units.map do |service_unit| - Service.new(service_unit, propmap) - end - end - - # Instantiate a SystemdService object based on the given name - # - # Use with caution as the service might exist or not. If you need to react when - # the service does not exist, use SystemdServiceClass.find. - # - # @param service_name [String] "foo" or "foo.service" - # @param propmap [SystemdUnit::PropMap] - # @return [Service] System service with the given name - def build(service_name, propmap = {}) - service_name += UNIT_SUFFIX unless service_name.end_with?(UNIT_SUFFIX) - Service.new(service_name, propmap) - end - - class Service < SystemdUnit - include Yast::Logger - - # Available only on installation system - START_SERVICE_INSTSYS_COMMAND = "/bin/service_start".freeze - - # @return [String] - def pid - properties.pid - end - - def running? - properties.running? - end - - def static? - properties.static? - end - - def start - command = "#{START_SERVICE_INSTSYS_COMMAND} #{unit_name}" - installation_system? ? run_instsys_command(command) : super - end - - def stop - command = "#{START_SERVICE_INSTSYS_COMMAND} --stop #{unit_name}" - installation_system? ? run_instsys_command(command) : super - end - - def restart - # Delegate to SystemdUnit#restart if not within installation - return super unless installation_system? - - stop - sleep(1) - start - end - - # Returns socket associated with service or nil if there is no such socket - # - # @return [Yast::SystemdSocketClass::Socket,nil] - # @see SystemdSocket.for_service - def socket - @socket ||= Yast::SystemdSocket.for_service(name) - end - - # Determines whether the service has an associated socket - # - # @return [Boolean] true if an associated socket exists; false otherwise. - def socket? - !socket.nil? - end - - private - - def installation_system? - File.exist?(START_SERVICE_INSTSYS_COMMAND) - end - - def run_instsys_command(command) - log.info("Running command '#{command}'") - error.clear - result = OpenStruct.new( - SCR.Execute(Path.new(".target.bash_output"), command) - ) - error << result.stderr - result.exit.zero? - end - end - end - SystemdService = SystemdServiceClass.new -end diff --git a/library/systemd/src/modules/systemd_socket.rb b/library/systemd/src/modules/systemd_socket.rb deleted file mode 100644 index 1b9bbedcb..000000000 --- a/library/systemd/src/modules/systemd_socket.rb +++ /dev/null @@ -1,109 +0,0 @@ -require "yast2/systemd_unit" -require "yast2/systemd_socket_finder" - -module Yast - ### - # Systemd.socket unit control API - # - # @example How to use it in other yast libraries - # - # require 'yast' - # - # Yast.import 'SystemdSocket' - # - # ## Get a socket unit by its name - # ## If the socket unit can't be found, you'll get a nil object - # - # socket = Yast::SystemdSocket.find('iscsid') # socket unit object - # - # ## If you can't handle any nil at the place of calling, - # ## use the finder with exclamation mark; - # ## SystemdSocketNotFound exception will be raised - # - # socket = Yast::SystemdSocket.find!('IcanHasCheez') # SystemdSocketNotFound: Socket unit 'IcanHasCheez' not found - # - # ## Get basic unit properties - # - # socket.unit_name # 'iscsid' - # socket.unit_type # 'socket' - # socket.id # 'iscsid.socket' - # socket.path # '/usr/lib/systemd/system/iscsid.socket' => unit file path - # socket.loaded? # true if it's loaded, false otherwise - # socket.listening? # true if it's listening, false otherwise - # socket.enabled? # true if enabled, false otherwise - # socket.disabled? # true if disabled, false otherwise - # socket.status # the same string output you get with `systemctl status iscsid.socket` - # socket.show # equivalent of calling `systemctl show iscsid.socket` - # - # ## Socket unit file commands - # - # # Unit file commands do modifications on the socket unit. Calling them triggers - # # socket properties reloading. In case a command fails, the error messages are available - # # through the method #errors as a string. - # - # socket.start # true if unit has been activated successfully - # socket.stop # true if unit has been deactivated successfully - # socket.enable # true if unit has been enabled successfully - # socket.disable # true if unit has been disabled successfully - # socket.errors # error string available if some of the actions above fails - # - # ## Extended socket properties - # - # # In case you need more details about the socket unit than the default ones, - # # you can extend the paramters when getting the socket. Those properties are - # # then available under the #properties instance method. To get an overview of - # # available socket properties, try e.g., `systemctl show iscsid.socket` - # - # socket = Yast::SystemdSocket.find('iscsid', :can_start=>'CanStart', :triggers=>'Triggers') - # socket.properties.can_start # 'yes' - # socket.properties.triggers # 'iscsid.service' - # - ## - - class SystemdSocketNotFound < StandardError - def initialize(socket_name) - super "Socket unit '#{socket_name}' not found" - end - end - - class SystemdSocketClass < Module - UNIT_SUFFIX = ".socket".freeze - - # @param propmap [SystemdUnit::PropMap] - def find(socket_name, propmap = {}) - socket_name += UNIT_SUFFIX unless socket_name.end_with?(UNIT_SUFFIX) - socket = Socket.new(socket_name, propmap) - return nil if socket.properties.not_found? - socket - end - - # @param propmap [SystemdUnit::PropMap] - def find!(socket_name, propmap = {}) - find(socket_name, propmap) || raise(SystemdSocketNotFound, socket_name) - end - - # @param propmap [SystemdUnit::PropMap] - def all(propmap = {}) - sockets = Systemctl.socket_units.map do |socket_unit| - Socket.new(socket_unit, propmap) - end - sockets.select { |s| s.properties.supported? } - end - - # Returns the socket for a given service - # - # @param service_name [String] Service name (without the `.service` extension) - # @return [Yast::SystemdSocketClass::Socket,nil] - def for_service(service_name) - @socket_finder ||= Yast2::SystemdSocketFinder.new - @socket_finder.for_service(service_name) - end - - class Socket < SystemdUnit - def listening? - properties.sub_state == "listening" - end - end - end - SystemdSocket = SystemdSocketClass.new -end diff --git a/library/systemd/src/modules/systemd_target.rb b/library/systemd/src/modules/systemd_target.rb deleted file mode 100644 index e3b214971..000000000 --- a/library/systemd/src/modules/systemd_target.rb +++ /dev/null @@ -1,121 +0,0 @@ -require "yast2/systemd_unit" - -module Yast - ### - # Systemd.target unit control API - # @example How to find a custom systemd target - # - # require 'yast' - # - # Yast.import 'SystemdTarget' - # - # # This will return either a target object or nil - # target = Yast::SystemdTarget.find('graphical') - # - # # This returns target object or raises exception SystemdTargetNotFound - # target = Yast::SystemdTarget.find!('whatever') - # - # # This returns collection of all available targets - # Yast::SystemdTarget.all - # - # @example How to find the current default target - # - # target = Yast::SystemdTarget.get_default - # target.unit_name # name of the default target - # target.allow_isolate? # should return true - # - # @example Set the default target - # - # Yast::SystemdTarget.set_default('multi-user') # returns true if success - # - # # Or if we have already an target object, use this for default target - # target = Yast::SystemdTarget.find('graphical') - # target.allow_isolate? # must be true to set default target - # target.set_default # returns true if success - ### - - class SystemdTargetNotFound < StandardError - def initialize(target_name) - super "Target unit '#{target_name}' not found" - end - end - - class SystemdTargetClass < Module - include Yast::Logger - - UNIT_SUFFIX = ".target".freeze - DEFAULT_TARGET = "default.target".freeze - # @return [SystemdUnit::PropMap] - PROPMAP = { allow_isolate: "AllowIsolate" }.freeze - - # @param propmap [SystemdUnit::PropMap] - def find(target_name, propmap = {}) - target_name += UNIT_SUFFIX unless target_name.end_with?(UNIT_SUFFIX) - target = Target.new(target_name, PROPMAP.merge(propmap)) - - if target.properties.not_found? - log.error "Target #{target_name} not found: #{target.properties.inspect}" - return nil - end - - target - end - - # @param propmap [SystemdUnit::PropMap] - def find!(target_name, propmap = {}) - find(target_name, propmap) || raise(SystemdTargetNotFound, target_name) - end - - # @param propmap [SystemdUnit::PropMap] - def all(propmap = {}) - targets = Systemctl.target_units.map do |target_unit_name| - find(target_unit_name, propmap) - end - targets.compact - end - - def get_default - result = Systemctl.execute("get-default") - raise(SystemctlError, result) unless result.exit.zero? - - find(result.stdout.strip) - end - - def set_default(target) - target_unit = target.is_a?(Target) ? target : find(target) - - unless target_unit - log.error "Cannot find target #{target.inspect}" - return false - end - - target_unit.set_default - end - - class Target < SystemdUnit - # Disable unsupported methods for target units - undef_method :start, :stop, :enable, :disable, :restart - - def allow_isolate? - # We cannot find out a target properties from /mnt in inst-sys - # systemctl doesn't return any properties in chroot - # See bnc#889323 - ["yes", nil].include?(properties.allow_isolate) - end - - def set_default - unless allow_isolate? - log.error "Cannot set #{id.inspect} as default target: Cannot be isolated (#{properties.allow_isolate})" - return false - end - - # Constructing a fallback target ID if we can't get it from systemctl - target_name = id ? id : "#{name}.target" - - result = Systemctl.execute("set-default --force #{target_name}") - result.exit.zero? - end - end - end - SystemdTarget = SystemdTargetClass.new -end diff --git a/library/systemd/test/Makefile.am b/library/systemd/test/Makefile.am index 3e014ce6a..340375223 100644 --- a/library/systemd/test/Makefile.am +++ b/library/systemd/test/Makefile.am @@ -1,18 +1,2 @@ -TESTS = \ - systemctl_test.rb \ - systemd_unit_test.rb \ - systemd_socket_test.rb \ - systemd_target_test.rb \ - systemd_service_test.rb \ - yast2/compound_service_test.rb \ - yast2/service_widget_test.rb \ - yast2/system_service_test.rb \ - yast2/system_service_test.rb \ - yast2/systemd_socket_finder_test.rb - -TEST_EXTENSIONS = .rb -RB_LOG_COMPILER = rspec -VERBOSE = 1 - -EXTRA_DIST = $(TESTS) test_helper.rb +EXTRA_DIST = test_helper.rb diff --git a/library/systemd/test/test_helper.rb b/library/systemd/test/test_helper.rb index 618c23e2e..791202e5c 100644 --- a/library/systemd/test/test_helper.rb +++ b/library/systemd/test/test_helper.rb @@ -1,10 +1,12 @@ require_relative "../../../test/test_helper.rb" -require "yast2/systemd_unit" +require "yast2/systemd/unit" +require "yast2/systemd/unit_properties" +require "yast2/systemd/service" +require "yast2/systemd/socket" +require "yast2/systemd/target" -Yast.import "SystemdSocket" -Yast.import "SystemdService" -Yast.import "SystemdTarget" +SYSTEMD_DATA_PATH = File.expand_path("data", __dir__) # Find a Term in given content # @@ -38,7 +40,7 @@ def stub_systemctl(unit) end def stub_execute(success: true) - allow(Yast::Systemctl).to receive(:execute).and_return( + allow(Yast2::Systemctl).to receive(:execute).and_return( OpenStruct.new( stdout: "success", stderr: (success ? "" : "failure"), @@ -48,7 +50,7 @@ def stub_execute(success: true) end def stub_socket_unit_files - allow(Yast::Systemctl).to receive(:list_unit_files).and_return(< 1, "stderr" => "", "stdout" => "" ) result = Systemctl.execute("enable cups.service") @@ -21,10 +21,10 @@ module Yast end it "raises exception if the execution has timed out" do - stub_const("Yast::Systemctl::TIMEOUT", 1) - allow(SCR).to receive(:Execute) { sleep 5 } - expect(SCR).to receive(:Execute) - expect { Systemctl.execute("disable cups.service") }.to raise_error(SystemctlError) + stub_const("Yast2::Systemctl::TIMEOUT", 1) + allow(Yast::SCR).to receive(:Execute) { sleep 5 } + expect(Yast::SCR).to receive(:Execute) + expect { Systemctl.execute("disable cups.service") }.to raise_error(Systemctl::Error) end end diff --git a/library/systemd/test/systemd_service_test.rb b/library/systemd/test/yast2/systemd_service_test.rb similarity index 67% rename from library/systemd/test/systemd_service_test.rb rename to library/systemd/test/yast2/systemd_service_test.rb index da731ec85..ce43cebfd 100755 --- a/library/systemd/test/systemd_service_test.rb +++ b/library/systemd/test/yast2/systemd_service_test.rb @@ -1,11 +1,9 @@ #!/usr/bin/env rspec -require_relative "test_helper" +require_relative "../test_helper" -module Yast - import "SystemdService" - - describe SystemdService do +module Yast2 + describe Systemd::Service do include SystemdServiceStubs before do @@ -15,8 +13,8 @@ module Yast describe ".find" do it "returns the service unit object specified in parameter" do ["sshd", "sshd.service"].each do |service_name| - service = SystemdService.find(service_name) - expect(service).to be_a(SystemdUnit) + service = Systemd::Service.find(service_name) + expect(service).to be_a(Systemd::Unit) expect(service.unit_type).to eq("service") expect(service.unit_name).to eq("sshd") end @@ -27,13 +25,13 @@ module Yast properties = OpenStruct.new( stdout: "", stderr: "Unit unknown.service could not be found.", exit: 1 ) - allow_any_instance_of(Yast::SystemdUnit::Properties) + allow_any_instance_of(Systemd::UnitProperties) .to receive(:load_systemd_properties) .and_return(properties) end it "returns nil" do - service = SystemdService.find("another") + service = Systemd::Service.find("another") expect(service).to be_nil end end @@ -42,8 +40,8 @@ module Yast describe ".build" do it "returns the service unit object specified in parameter" do ["sshd", "sshd.service"].each do |service_name| - service = SystemdService.build(service_name) - expect(service).to be_a(SystemdUnit) + service = Systemd::Service.build(service_name) + expect(service).to be_a(Systemd::Unit) expect(service.unit_type).to eq("service") expect(service.unit_name).to eq("sshd") end @@ -51,22 +49,22 @@ module Yast it "returns a service instance even if the real service does not exist" do stub_services(service: "unknown") - service = SystemdService.build("unknown") + service = Systemd::Service.build("unknown") expect(service.name).to eq("unknown") end end describe ".find!" do it "returns the service unit object specified in parameter" do - service = SystemdService.find("sshd") - expect(service).to be_a(SystemdUnit) + service = Systemd::Service.find("sshd") + expect(service).to be_a(Systemd::Unit) expect(service.unit_type).to eq("service") expect(service.unit_name).to eq("sshd") end - it "raises SystemdServiceNotFound error if unit does not exist" do + it "raises Systemd::ServiceNotFound error if unit does not exist" do stub_services(service: "unknown") - expect { SystemdService.find!("unknown") }.to raise_error(SystemdServiceNotFound) + expect { Systemd::Service.find!("unknown") }.to raise_error(Systemd::ServiceNotFound) end end @@ -75,20 +73,20 @@ module Yast let(:apparmor_double) { double("Service", name: "apparmor") } let(:cups_double) { double("Service", name: "cups") } let(:systemctl_stdout) do - File.read(File.join(__dir__, "data", "apparmor_and_cups_properties")) + File.read(File.join(SYSTEMD_DATA_PATH, "apparmor_and_cups_properties")) end before do - allow(Yast::Systemctl).to receive(:execute).with( + allow(Yast2::Systemctl).to receive(:execute).with( "show --property=Id,MainPID,Description,LoadState,ActiveState,SubState,UnitFileState," \ "FragmentPath,CanReload apparmor.service cups.service" ).and_return(systemctl_show) - allow(SystemdService).to receive(:find).with("apparmor", {}).and_return(apparmor_double) - allow(SystemdService).to receive(:find).with("cups", {}).and_return(cups_double) + allow(Systemd::Service).to receive(:find).with("apparmor", {}).and_return(apparmor_double) + allow(Systemd::Service).to receive(:find).with("cups", {}).and_return(cups_double) end it "returns the list of services" do - services = SystemdService.find_many(["apparmor", "cups"]) + services = Systemd::Service.find_many(["apparmor", "cups"]) expect(services).to contain_exactly( an_object_having_attributes("name" => "apparmor"), an_object_having_attributes("name" => "cups") @@ -99,14 +97,14 @@ module Yast let(:not_found_double) { double("Service", name: "cups", not_found?: true) } before do - allow(Yast::SystemdServiceClass::Service).to receive(:new).and_call_original - allow(Yast::SystemdServiceClass::Service).to receive(:new) + allow(Systemd::Service).to receive(:new).and_call_original + allow(Systemd::Service).to receive(:new) .with("cups.service", anything, anything) .and_return(not_found_double) end it "does not include the not found service" do - services = SystemdService.find_many(["apparmor", "cups"]) + services = Systemd::Service.find_many(["apparmor", "cups"]) expect(services.map(&:name)).to eq(["apparmor"]) end end @@ -115,7 +113,7 @@ module Yast let(:systemctl_show) { OpenStruct.new(stdout: "", stderr: "", exit: 1) } it "retrieve services information in a one-by-one basis" do - expect(SystemdService.find_many(["apparmor", "cups"])) + expect(Systemd::Service.find_many(["apparmor", "cups"])) .to eq([apparmor_double, cups_double]) end end @@ -124,7 +122,7 @@ module Yast let(:systemctl_show) { OpenStruct.new(stdout: "", stderr: "error", exit: 1) } it "retrieve services information in a one-by-one basis" do - expect(SystemdService.find_many(["apparmor", "cups"])) + expect(Systemd::Service.find_many(["apparmor", "cups"])) .to eq([apparmor_double, cups_double]) end end @@ -132,7 +130,7 @@ module Yast describe ".all" do it "returns all supported services found" do - services = SystemdService.all + services = Systemd::Service.all expect(services).to be_a(Array) expect(services).not_to be_empty services.each { |s| expect(s.unit_type).to eq("service") } @@ -141,7 +139,7 @@ module Yast describe "#running?" do it "returns true if the service is running" do - service = SystemdService.find "sshd" + service = Systemd::Service.find "sshd" expect(service).to respond_to(:running?) expect(service.running?).to eq(true) end @@ -149,7 +147,7 @@ module Yast describe "#pid" do it "returns the pid of the running service" do - service = SystemdService.find("sshd") + service = Systemd::Service.find("sshd") expect(service).to respond_to(:pid) expect(service.pid).not_to be_empty end @@ -158,22 +156,22 @@ module Yast context "Start a service on the installation system" do it "starts a service with a specialized inst-sys helper if available" do allow(File).to receive(:exist?).with("/bin/service_start").and_return(true) - service = SystemdService.find("sshd") - allow(SCR).to receive(:Execute).and_return("stderr" => "", "stdout" => "", "exit" => 0) - expect(service).not_to receive(:command) # SystemdUnit#command + service = Systemd::Service.find("sshd") + allow(Yast::SCR).to receive(:Execute).and_return("stderr" => "", "stdout" => "", "exit" => 0) + expect(service).not_to receive(:command) # Systemd::Unit#command expect(service.start).to eq(true) end end context "Restart a service on the installation system" do it "restarts a service with a specialized inst-sys helper if available" do - allow_any_instance_of(SystemdServiceClass::Service).to receive(:sleep).and_return(1) + allow_any_instance_of(Systemd::Service).to receive(:sleep).and_return(1) allow(File).to receive(:exist?).with("/bin/service_start").and_return(true) - service = SystemdService.find("sshd") - allow(SCR).to receive(:Execute).and_return("stderr" => "", "stdout" => "", "exit" => 0) + service = Systemd::Service.find("sshd") + allow(Yast::SCR).to receive(:Execute).and_return("stderr" => "", "stdout" => "", "exit" => 0) expect(service).to receive(:stop).ordered.and_call_original expect(service).to receive(:start).ordered.and_call_original - expect(service).not_to receive(:command) # SystemdUnit#command + expect(service).not_to receive(:command) # Systemd::Unit#command expect(service.restart).to eq(true) end end @@ -181,26 +179,26 @@ module Yast context "Stop a service on the installation system" do it "stops a service with a specialized inst-sys helper" do allow(File).to receive(:exist?).with("/bin/service_start").and_return(true) - service = SystemdService.find("sshd") - allow(SCR).to receive(:Execute).and_return("stderr" => "", "stdout" => "", "exit" => 0) - expect(service).not_to receive(:command) # SystemdUnit#command + service = Systemd::Service.find("sshd") + allow(Yast::SCR).to receive(:Execute).and_return("stderr" => "", "stdout" => "", "exit" => 0) + expect(service).not_to receive(:command) # Systemd::Unit#command expect(service.stop).to eq(true) end end describe "#socket" do - subject(:service) { SystemdService.find(service_name) } + subject(:service) { Systemd::Service.find(service_name) } let(:service_name) { "sshd" } - let(:socket) { instance_double(SystemdSocketClass::Socket) } + let(:socket) { instance_double(Systemd::Socket) } it "returns the socket for the service" do - expect(SystemdSocket).to receive(:for_service).with(service_name) + expect(Systemd::Socket).to receive(:for_service).with(service_name) .and_return(socket) expect(service.socket).to eq(socket) end it "asks for the socket only once" do - expect(SystemdSocket).to receive(:for_service).with(service_name) + expect(Systemd::Socket).to receive(:for_service).with(service_name) .and_return(socket).once expect(service.socket).to eq(socket) expect(service.socket).to eq(socket) @@ -208,7 +206,7 @@ module Yast context "when no associated socket is found" do before do - allow(SystemdSocket).to receive(:for_service).with(service_name) + allow(Systemd::Socket).to receive(:for_service).with(service_name) .and_return(nil) end @@ -219,14 +217,14 @@ module Yast end describe "#socket?" do - subject(:service) { SystemdService.find("sshd") } + subject(:service) { Systemd::Service.find("sshd") } before do allow(service).to receive(:socket).and_return(socket) end context "when there is an associated socket" do - let(:socket) { instance_double(SystemdSocketClass::Socket) } + let(:socket) { instance_double(Systemd::Socket) } it "returns true" do expect(service.socket?).to eq(true) diff --git a/library/systemd/test/yast2/systemd_socket_finder_test.rb b/library/systemd/test/yast2/systemd_socket_finder_test.rb index 214f0506d..d4126d3c9 100644 --- a/library/systemd/test/yast2/systemd_socket_finder_test.rb +++ b/library/systemd/test/yast2/systemd_socket_finder_test.rb @@ -21,9 +21,9 @@ # find current contact information at www.suse.com. require_relative "../test_helper" -require "yast2/systemd_socket_finder" +require "yast2/systemd/socket_finder" -describe Yast2::SystemdSocketFinder do +describe Yast2::Systemd::SocketFinder do subject(:finder) { described_class.new } let(:systemctl_result) do @@ -34,7 +34,7 @@ end before do - allow(Yast::Systemctl).to receive(:execute) + allow(Yast2::Systemctl).to receive(:execute) .with("show --property Id,Triggers cups.socket sckt1.socket") .and_return(systemctl_result) allow(Yast::Execute).to receive(:on_target).and_return( @@ -48,7 +48,7 @@ let(:socket) { double("socket") } it "returns the related socket" do - expect(Yast::SystemdSocket).to receive(:find).with("sckt1").and_return(socket) + expect(Yast2::Systemd::Socket).to receive(:find).with("sckt1").and_return(socket) expect(finder.for_service("srvc1")).to eq(socket) end @@ -58,7 +58,7 @@ end it "does not try to read the socket" do - expect(Yast::SystemdSocket).to_not receive(:find) + expect(Yast2::Systemd::Socket).to_not receive(:find) expect(finder.for_service("httpd")).to be_nil end end @@ -69,13 +69,13 @@ end it "returns a socket named after the service" do - expect(Yast::SystemdSocket).to receive(:find).with("cups").and_return(socket) + expect(Yast2::Systemd::Socket).to receive(:find).with("cups").and_return(socket) expect(finder.for_service("cups")).to eq(socket) end context "when there is no related socket" do before do - allow(Yast::SystemdSocket).to receive(:find).with("httpd").and_return(nil) + allow(Yast2::Systemd::Socket).to receive(:find).with("httpd").and_return(nil) end it "returns nil" do diff --git a/library/systemd/test/systemd_socket_test.rb b/library/systemd/test/yast2/systemd_socket_test.rb similarity index 63% rename from library/systemd/test/systemd_socket_test.rb rename to library/systemd/test/yast2/systemd_socket_test.rb index efca78329..39b59351c 100755 --- a/library/systemd/test/systemd_socket_test.rb +++ b/library/systemd/test/yast2/systemd_socket_test.rb @@ -1,23 +1,21 @@ #!/usr/bin/env rspec -require_relative "test_helper" - -module Yast - import "SystemdSocket" - - describe SystemdSocket do - subject(:systemd_socket) { SystemdSocketClass.new } +require_relative "../test_helper" +module Yast2 + describe Systemd::Socket do include SystemdSocketStubs + subject(:systemd_socket) { described_class } + before do stub_sockets end describe ".find" do it "returns the unit object specified in parameter" do - socket = SystemdSocket.find "iscsid" - expect(socket).to be_a(SystemdUnit) + socket = systemd_socket.find "iscsid" + expect(socket).to be_a(Systemd::Unit) expect(socket.unit_type).to eq("socket") expect(socket.unit_name).to eq("iscsid") end @@ -25,32 +23,33 @@ module Yast describe ".find!" do it "returns the unit object specified in parameter" do - socket = SystemdSocket.find "iscsid" - expect(socket).to be_a(SystemdUnit) + socket = systemd_socket.find "iscsid" + expect(socket).to be_a(Systemd::Unit) expect(socket.unit_type).to eq("socket") expect(socket.unit_name).to eq("iscsid") end - it "raises SystemdSocketNotFound error if unit does not exist" do + it "raises Systemd::SocketNotFound error if unit does not exist" do stub_sockets(socket: "unknown") - expect { SystemdSocket.find!("unknown") }.to raise_error(SystemdSocketNotFound) + expect { systemd_socket.find!("unknown") }.to raise_error(Systemd::SocketNotFound) end end describe ".all" do it "returns all supported sockets found" do - sockets = SystemdSocket.all + sockets = systemd_socket.all expect(sockets).to be_a(Array) expect(sockets).not_to be_empty sockets.each { |s| expect(s.unit_type).to eq("socket") } end describe ".for_service" do - let(:finder) { instance_double(Yast2::SystemdSocketFinder, for_service: socket) } - let(:socket) { instance_double(SystemdSocketClass::Socket) } + let(:finder) { instance_double(Yast2::Systemd::SocketFinder, for_service: socket) } + let(:socket) { instance_double(Systemd::Socket) } before do - allow(Yast2::SystemdSocketFinder).to receive(:new).and_return(finder) + subject.reset + allow(Yast2::Systemd::SocketFinder).to receive(:new).and_return(finder) end it "returns the socket for the given service" do @@ -69,13 +68,15 @@ module Yast describe "#listening?" do it "returns true if the socket is accepting connections" do - socket = SystemdSocket.find "iscsid" + socket = systemd_socket.find "iscsid" + expect(socket.listening?).to eq(true) end it "returns false if the socket is dead" do - socket = SystemdSocket.find "iscsid" + socket = systemd_socket.find "iscsid" socket.properties.sub_state = "dead" + expect(socket.listening?).to eq(false) end end diff --git a/library/systemd/test/systemd_target_test.rb b/library/systemd/test/yast2/systemd_target_test.rb similarity index 77% rename from library/systemd/test/systemd_target_test.rb rename to library/systemd/test/yast2/systemd_target_test.rb index 93b966131..583674981 100755 --- a/library/systemd/test/systemd_target_test.rb +++ b/library/systemd/test/yast2/systemd_target_test.rb @@ -1,11 +1,9 @@ #!/usr/bin/env rspec -require_relative "test_helper" +require_relative "../test_helper" -module Yast - import "SystemdTarget" - - describe SystemdTarget do +module Yast2 + describe Systemd::Target do include SystemdTargetStubs before do @@ -14,35 +12,35 @@ module Yast describe ".find" do it "returns the target unit object specified in parameter" do - target = SystemdTarget.find("graphical") + target = Systemd::Target.find("graphical") expect(target).not_to be_nil - expect(target).to be_a(SystemdTargetClass::Target) + expect(target).to be_a(Systemd::Target) expect(target.unit_type).to eq("target") end it "returns nil if the target unit does not exist" do stub_targets(target: "unknown") - target = SystemdTarget.find("unknown") + target = Systemd::Target.find("unknown") expect(target).to be_nil end end describe ".find!" do it "returns the target unit object specified in parameter" do - target = SystemdTarget.find("graphical") + target = Systemd::Target.find("graphical") expect(target).not_to be_nil expect(target.unit_type).to eq("target") end - it "raises SystemdTargetNotFound error if the target unit does not exist" do + it "raises Systemd::TargetNotFound error if the target unit does not exist" do stub_targets(target: "unknown") - expect { SystemdTarget.find!("unknown") }.to raise_error(SystemdTargetNotFound) + expect { Systemd::Target.find!("unknown") }.to raise_error(Systemd::TargetNotFound) end end describe ".all" do it "returns all targets found" do - targets = SystemdTarget.all + targets = Systemd::Target.all expect(targets).to be_a(Array) expect(targets).not_to be_empty expect(targets).not_to include(nil) @@ -52,7 +50,7 @@ module Yast describe "methods not supported for target units" do it "raises NoMethodError for unsupported unit methods" do - target = SystemdTarget.find("graphical") + target = Systemd::Target.find("graphical") expect(target).not_to be_nil expect { target.enable }.to raise_error(NoMethodError) expect { target.disable }.to raise_error(NoMethodError) @@ -66,9 +64,9 @@ module Yast allow(Systemctl).to receive(:execute).with("get-default").and_return( OpenStruct.new("exit" => 0, "stdout" => "graphical.target", "stderr" => "") ) - target = SystemdTarget.get_default + target = Systemd::Target.get_default expect(target).not_to be_nil - expect(target).to be_a(SystemdTargetClass::Target) + expect(target).to be_a(Systemd::Target) expect(target.unit_name).to eq("graphical") end end @@ -77,12 +75,12 @@ module Yast it "returns true if the default target has been has for the parameter successfully" do expect(Systemctl).to receive(:execute).with("set-default --force graphical.target") .and_return(OpenStruct.new("exit" => 0, "stdout" => "", "stderr" => "")) - expect(SystemdTarget.set_default("graphical")).to eq(true) + expect(Systemd::Target.set_default("graphical")).to eq(true) end it "returns false if the default target has not been set" do stub_targets(target: "unknown") - expect(SystemdTarget.set_default("unknown")).to eq(false) + expect(Systemd::Target.set_default("unknown")).to eq(false) end end @@ -90,13 +88,13 @@ module Yast it "it returns true if the target unit object has been set as default target" do expect(Systemctl).to receive(:execute).with("set-default --force graphical.target") .and_return(OpenStruct.new("exit" => 0, "stdout" => "", "stderr" => "")) - target = SystemdTarget.find("graphical") + target = Systemd::Target.find("graphical") expect(target.set_default).to eq(true) end it "returns false if the target unit has not been set as default target" do stub_targets(target: "network") - target = SystemdTarget.find("network") + target = Systemd::Target.find("network") expect(target.set_default).to eq(false) end @@ -105,7 +103,7 @@ module Yast expect(Systemctl).to receive(:execute).with("set-default --force multi-user-in-installation.target") .and_return(OpenStruct.new("exit" => 0, "stdout" => "", "stderr" => "")) stub_targets(target: "multi-user-in-installation") - target = SystemdTarget.find("multi-user-in-installation") + target = Systemd::Target.find("multi-user-in-installation") expect(target.set_default).to eq(true) end end @@ -113,20 +111,20 @@ module Yast describe "#allow_isolate?" do it "returns true if the unit is allowed to be isolated" do - target = SystemdTarget.find("graphical") + target = Systemd::Target.find("graphical") expect(target.allow_isolate?).to eq(true) end it "returns false if the unit is not allowed to be isolated" do stub_targets(target: "network") - target = SystemdTarget.find("network") + target = Systemd::Target.find("network") expect(target.allow_isolate?).to eq(false) end context "when target properties cannot be found out (e.g. in chroot)" do it "returns true" do stub_targets(target: "multi-user-in-installation") - target = SystemdTarget.find("multi-user-in-installation") + target = Systemd::Target.find("multi-user-in-installation") expect(target.allow_isolate?).to eq(true) end end diff --git a/library/systemd/test/systemd_unit_test.rb b/library/systemd/test/yast2/systemd_unit_test.rb similarity index 75% rename from library/systemd/test/systemd_unit_test.rb rename to library/systemd/test/yast2/systemd_unit_test.rb index 9a35461e0..f2bf0fc98 100755 --- a/library/systemd/test/systemd_unit_test.rb +++ b/library/systemd/test/yast2/systemd_unit_test.rb @@ -1,14 +1,14 @@ #!/usr/bin/env rspec -require_relative "test_helper" +require_relative "../test_helper" -module Yast - describe SystemdUnit do +module Yast2 + describe Systemd::Unit do include SystemdSocketStubs include SystemdServiceStubs def trigger_reloading_properties(command) - unit = SystemdUnit.new("new.socket") + unit = Systemd::Unit.new("new.socket") properties = unit.properties unit.send(command) expect(unit.properties).not_to equal(properties) @@ -27,32 +27,32 @@ def trigger_reloading_properties(command) describe "#properties" do context "Unit found" do it "returns struct with restricted installation properties" do - allow_any_instance_of(SystemdUnit).to receive(:command) + allow_any_instance_of(Systemd::Unit).to receive(:command) .with("is-enabled sshd.service").and_return( OpenStruct.new("stderr" => "", "stdout" => "enabled", "exit" => 0) ) - unit = SystemdUnit.new("sshd.service") - expect(unit.properties).to be_a(SystemdUnit::InstallationProperties) + unit = Systemd::Unit.new("sshd.service") + expect(unit.properties).to be_a(Systemd::UnitInstallationProperties) expect(unit.properties.not_found?).to eq(false) end describe "#enabled?" do it "returns true if service is enabled" do - allow_any_instance_of(SystemdUnit).to receive(:command) + allow_any_instance_of(Systemd::Unit).to receive(:command) .with("is-enabled sshd.service").and_return( OpenStruct.new("stderr" => "", "stdout" => "enabled", "exit" => 0) ) - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.enabled?).to eq(true) end it "returns false if service is disabled" do stub_unit_command(success: false) - allow_any_instance_of(SystemdUnit).to receive(:command) + allow_any_instance_of(Systemd::Unit).to receive(:command) .with("is-enabled sshd.service").and_return( OpenStruct.new("stderr" => "", "stdout" => "disabled", "exit" => 1) ) - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.enabled?).to eq(false) end end @@ -62,8 +62,8 @@ def trigger_reloading_properties(command) it "returns struct with restricted installation properties" do stub_services(service: "unknown") stub_unit_command(success: false) - unit = SystemdUnit.new("unknown.service") - expect(unit.properties).to be_a(SystemdUnit::InstallationProperties) + unit = Systemd::Unit.new("unknown.service") + expect(unit.properties).to be_a(Systemd::UnitInstallationProperties) expect(unit.properties.not_found?).to eq(true) end end @@ -72,12 +72,12 @@ def trigger_reloading_properties(command) describe "#properties" do it "always returns struct including default properties" do - unit = SystemdUnit.new("iscsi.socket") - expect(unit.properties.to_h.keys).to include(*SystemdUnit::DEFAULT_PROPMAP.keys) + unit = Systemd::Unit.new("iscsi.socket") + expect(unit.properties.to_h.keys).to include(*Yast2::Systemd::UnitPropMap::DEFAULT.keys) end it "provides status properties methods" do - unit = SystemdUnit.new("iscsid.socket") + unit = Systemd::Unit.new("iscsid.socket") expect(unit.properties[:enabled?]).not_to be_nil expect(unit.properties[:active?]).not_to be_nil expect(unit.properties[:loaded?]).not_to be_nil @@ -91,7 +91,7 @@ def trigger_reloading_properties(command) end it "delegates the status properties onto the unit object" do - unit = SystemdUnit.new("iscsid.socket") + unit = Systemd::Unit.new("iscsid.socket") expect(unit).to respond_to(:enabled?) expect(unit).to respond_to(:active?) expect(unit).to respond_to(:loaded?) @@ -101,32 +101,32 @@ def trigger_reloading_properties(command) end describe ".new" do - it "creates a new SystemdUnit instance with unit name and type parsed from first parameter" do + it "creates a new Systemd::Unit instance with unit name and type parsed from first parameter" do instance = nil - expect { instance = SystemdUnit.new("random.socket") }.not_to raise_error + expect { instance = Systemd::Unit.new("random.socket") }.not_to raise_error expect(instance.unit_name).to eq("random") expect(instance.unit_type).to eq("socket") end it "correctly parses a name with many dots" do instance = nil - expect { instance = SystemdUnit.new("dbus-org.freedesktop.hostname1.service") }.not_to raise_error + expect { instance = Systemd::Unit.new("dbus-org.freedesktop.hostname1.service") }.not_to raise_error expect(instance.unit_name).to eq("dbus-org.freedesktop.hostname1") expect(instance.unit_type).to eq("service") end it "raises an exception if an incomplete unit name is passed" do - expect { SystemdUnit.new("sshd") }.to raise_error(RuntimeError) + expect { Systemd::Unit.new("sshd") }.to raise_error(RuntimeError) end it "allows to create supported units" do - expect { SystemdUnit.new("my.socket") }.not_to raise_error - expect { SystemdUnit.new("default.target") }.not_to raise_error - expect { SystemdUnit.new("sshd.service") }.not_to raise_error + expect { Systemd::Unit.new("my.socket") }.not_to raise_error + expect { Systemd::Unit.new("default.target") }.not_to raise_error + expect { Systemd::Unit.new("sshd.service") }.not_to raise_error end it "accepts parameters to extend the default properties" do - unit = SystemdUnit.new("iscsid.socket", requires: "Requires", wants: "Wants") + unit = Systemd::Unit.new("iscsid.socket", requires: "Requires", wants: "Wants") expect(unit.properties.wants).not_to be_nil expect(unit.properties.requires).not_to be_nil end @@ -135,13 +135,13 @@ def trigger_reloading_properties(command) describe "#stop" do it "returns true if unit has been stopped" do stub_unit_command - unit = SystemdUnit.new("my.socket") + unit = Systemd::Unit.new("my.socket") expect(unit.stop).to eq(true) end it "returns false if failed" do stub_unit_command(success: false) - unit = SystemdUnit.new("my.socket") + unit = Systemd::Unit.new("my.socket") expect(unit.stop).to eq(false) expect(unit.error).not_to be_empty end @@ -154,13 +154,13 @@ def trigger_reloading_properties(command) describe "#start" do it "returns true if starts (activates) the unit" do stub_unit_command(success: true) - unit = SystemdUnit.new("my.socket") + unit = Systemd::Unit.new("my.socket") expect(unit.start).to eq(true) end it "returns false if failed to start the unit" do stub_unit_command(success: false) - unit = SystemdUnit.new("my.socket") + unit = Systemd::Unit.new("my.socket") expect(unit.start).to eq(false) expect(unit.error).not_to be_empty end @@ -173,13 +173,13 @@ def trigger_reloading_properties(command) describe "#enable" do it "returns true if the unit has been enabled successfully" do stub_unit_command(success: true) - unit = SystemdUnit.new("your.socket") + unit = Systemd::Unit.new("your.socket") expect(unit.enable).to eq(true) end it "returns false if unit fails" do stub_unit_command(success: false) - unit = SystemdUnit.new("your.socket") + unit = Systemd::Unit.new("your.socket") expect(unit.enable).to eq(false) end @@ -191,13 +191,13 @@ def trigger_reloading_properties(command) describe "#disable" do it "returns true if the unit has been disabled successfully" do stub_unit_command(success: true) - unit = SystemdUnit.new("your.socket") + unit = Systemd::Unit.new("your.socket") expect(unit.disable).to eq(true) end it "returns false if unit disabling fails" do stub_unit_command(success: false) - unit = SystemdUnit.new("your.socket") + unit = Systemd::Unit.new("your.socket") expect(unit.disable).to eq(false) end @@ -208,14 +208,14 @@ def trigger_reloading_properties(command) describe "#show" do it "always returns new unit properties object" do - unit = SystemdUnit.new("startrek.socket") + unit = Systemd::Unit.new("startrek.socket") expect(unit.show).not_to equal(unit.show) end end describe "#refresh!" do it "rewrites and returns the properties instance variable" do - unit = SystemdUnit.new("your.socket") + unit = Systemd::Unit.new("your.socket") properties = unit.properties expect(unit.refresh!).not_to equal(properties) end @@ -223,65 +223,65 @@ def trigger_reloading_properties(command) describe "#restart" do it "returns true if unit has been restarted" do - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.restart).to eq(true) end it "returns false if the restart fails" do stub_unit_command(success: false) - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.restart).to eq(false) end end describe "#try_restart" do it "returns true if the unit has been restarted" do - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.try_restart).to eq(true) end it "returns false if the try_restart fails" do stub_unit_command(success: false) - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.try_restart).to eq(false) end end describe "#reload" do it "returns true if the unit has been reloaded" do - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.reload).to eq(true) end it "returns false if the reload fails" do stub_unit_command(success: false) - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.reload).to eq(false) end end describe "#reload_or_restart" do it "returns true if the unit has been reloaded or restarted" do - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.reload_or_restart).to eq(true) end it "returns false if the reload_or_restart action fails" do stub_unit_command(success: false) - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.reload_or_restart).to eq(false) end end describe "#reload_or_try_restart" do it "returns true if the unit has been reload_or_try_restarted" do - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.reload_or_try_restart).to eq(true) end it "returns false if the reload_or_try_restart action fails" do stub_unit_command(success: false) - unit = SystemdUnit.new("sshd.service") + unit = Systemd::Unit.new("sshd.service") expect(unit.reload_or_try_restart).to eq(false) end end @@ -289,7 +289,7 @@ def trigger_reloading_properties(command) describe "#error" do it "returns empty string if the unit commands succeed" do stub_unit_command(success: true) - unit = SystemdUnit.new("your.socket") + unit = Systemd::Unit.new("your.socket") unit.start expect(unit.error).to be_empty @@ -305,7 +305,7 @@ def trigger_reloading_properties(command) it "returns error string if the unit commands fail" do stub_unit_command(success: false) - unit = SystemdUnit.new("your.socket") + unit = Systemd::Unit.new("your.socket") unit.start expect(unit.error).not_to be_empty diff --git a/package/yast2.changes b/package/yast2.changes index c3358ecae..a925af4da 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Tue Aug 21 14:15:07 UTC 2018 - dgonzalez@suse.com + +- Refactor systemd classes, reorganizing them in real classes + under the Yast2::Systemd namespace instead of using modules + (related to fate#319428). +- 4.1.3 + ------------------------------------------------------------------- Mon Aug 20 17:13:52 CEST 2018 - schubi@suse.de diff --git a/package/yast2.spec b/package/yast2.spec index ed4fdd7ca..3df59fc87 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.2 +Version: 4.1.3 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From bf5954da312f6390f959933e9ba535603b05f717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Fri, 24 Aug 2018 15:12:46 +0100 Subject: [PATCH 093/223] Fix Yast2::ServiceWidget to not show "On Boot" option (#805) * Fix Yast2::Systemd::UnitProperties#static? * Add missing Makefile.am in the systemd library * Add SystemService#support_start_on_boot? * ServiceWidget shows 'On Boot' option only when supported * Documentation updates * Bump version and update changes file --- library/cwm/src/modules/CWMServiceStart.rb | 2 +- library/general/src/lib/ui/service_status.rb | 2 + .../systemd/src/lib/yast2/service_widget.rb | 5 +- .../systemd/src/lib/yast2/system_service.rb | 7 + .../src/lib/yast2/systemd/unit_properties.rb | 8 +- library/systemd/test/Makefile.am | 13 +- .../systemd/test/data/tftp_service_properties | 196 ++++++++++++++++++ library/systemd/test/test_helper.rb | 2 +- .../systemd/test/yast2/service_widget_test.rb | 70 ++++++- .../systemd/test/yast2/system_service_test.rb | 18 ++ .../test/yast2/systemd_socket_finder_test.rb | 0 .../test/yast2/unit_properties_test.rb | 52 +++++ package/yast2.changes | 7 + package/yast2.spec | 2 +- 14 files changed, 369 insertions(+), 15 deletions(-) create mode 100644 library/systemd/test/data/tftp_service_properties mode change 100644 => 100755 library/systemd/test/yast2/systemd_socket_finder_test.rb create mode 100755 library/systemd/test/yast2/unit_properties_test.rb diff --git a/library/cwm/src/modules/CWMServiceStart.rb b/library/cwm/src/modules/CWMServiceStart.rb index fd67ffaa4..6c6af1636 100644 --- a/library/cwm/src/modules/CWMServiceStart.rb +++ b/library/cwm/src/modules/CWMServiceStart.rb @@ -27,7 +27,7 @@ module Yast # Routines for service start widget handling. # - # NOTE: a modern API for this is {::UI::ServiceStatus} + # @deprecated Use CWM::ServiceWidget instead. class CWMServiceStartClass < Module def main Yast.import "UI" diff --git a/library/general/src/lib/ui/service_status.rb b/library/general/src/lib/ui/service_status.rb index 4a159b742..5a68e9c68 100644 --- a/library/general/src/lib/ui/service_status.rb +++ b/library/general/src/lib/ui/service_status.rb @@ -29,6 +29,8 @@ module UI # It also provides checkboxes (reload_flag and enabled_flag) for the user # to specify whether the service must be reloaded/restarted after # configuration changes and whether it must be enabled at boot time. + # + # @deprecated Use CWM::ServiceWidget instead. class ServiceStatus include Yast::UIShortcuts include Yast::I18n diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 14b9af547..04e42069a 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -58,6 +58,9 @@ module Yast2 # @service.save # end # end + # + # @todo Allow to specify the widget ID. Currently, it uses always the same, so you can not use + # more than one instance of this class at the same time. class ServiceWidget include Yast::I18n include Yast::Logger @@ -194,7 +197,7 @@ def autostart_items system_start_mode = service.current_start_mode res = [] - res << Item(Id(:service_widget_autostart_on_boot), _("Start on boot"), current_start_mode == :on_boot) + res << Item(Id(:service_widget_autostart_on_boot), _("Start on boot"), current_start_mode == :on_boot) if service.support_start_on_boot? res << Item(Id(:service_widget_autostart_on_demand), _("Start on demand"), current_start_mode == :on_demand) if service.support_start_on_demand? res << Item(Id(:service_widget_autostart_manual), _("Do not start"), current_start_mode == :manual) res << Item(Id(:service_widget_autostart_inconsistent), _("Keep current settings"), current_start_mode == :inconsistent) if system_start_mode == :inconsistent diff --git a/library/systemd/src/lib/yast2/system_service.rb b/library/systemd/src/lib/yast2/system_service.rb index 445d38bdb..b049003dc 100644 --- a/library/systemd/src/lib/yast2/system_service.rb +++ b/library/systemd/src/lib/yast2/system_service.rb @@ -280,6 +280,13 @@ def support_start_on_demand? start_modes.include?(:on_demand) end + # Whether the service supports :on_boot start mode + # + # @return [Boolean] + def support_start_on_boot? + start_modes.include?(:on_boot) + end + # Whether the service will be active after calling {#save} # # @note This is a temporary value (not saved yet). Use {#currently_active?} to get the actual diff --git a/library/systemd/src/lib/yast2/systemd/unit_properties.rb b/library/systemd/src/lib/yast2/systemd/unit_properties.rb index d3b9def68..ff9560a0d 100644 --- a/library/systemd/src/lib/yast2/systemd/unit_properties.rb +++ b/library/systemd/src/lib/yast2/systemd/unit_properties.rb @@ -48,10 +48,10 @@ def initialize(systemd_unit, property_text) extract_properties self[:active?] = ACTIVE_STATES.include?(active_state) - self[:running?] = sub_state == "running" - self[:loaded?] = load_state == "loaded" - self[:not_found?] = load_state == "not-found" - self[:static?] = load_state == "static" + self[:running?] = sub_state == "running" + self[:loaded?] = load_state == "loaded" + self[:not_found?] = load_state == "not-found" + self[:static?] = unit_file_state == "static" self[:enabled?] = read_enabled_state self[:supported?] = SUPPORTED_STATES.include?(unit_file_state) self[:can_reload?] = can_reload == "yes" diff --git a/library/systemd/test/Makefile.am b/library/systemd/test/Makefile.am index 340375223..bfbb96649 100644 --- a/library/systemd/test/Makefile.am +++ b/library/systemd/test/Makefile.am @@ -1,2 +1,13 @@ -EXTRA_DIST = test_helper.rb +TESTS = \ + yast2/compound_service_test.rb \ + yast2/service_widget_test.rb \ + yast2/systemctl_test.rb \ + yast2/systemd_service_test.rb \ + yast2/systemd_socket_finder_test.rb \ + yast2/systemd_socket_test.rb \ + yast2/systemd_target_test.rb \ + yast2/systemd_unit_test.rb \ + yast2/system_service_test.rb \ + yast2/unit_properties_test.rb +EXTRA_DIST = $(TESTS) diff --git a/library/systemd/test/data/tftp_service_properties b/library/systemd/test/data/tftp_service_properties new file mode 100644 index 000000000..a9e086dc2 --- /dev/null +++ b/library/systemd/test/data/tftp_service_properties @@ -0,0 +1,196 @@ +Type=simple +Restart=no +NotifyAccess=none +RestartUSec=100ms +TimeoutStartUSec=1min 30s +TimeoutStopUSec=1min 30s +RuntimeMaxUSec=infinity +WatchdogUSec=0 +WatchdogTimestampMonotonic=0 +PermissionsStartOnly=no +RootDirectoryStartOnly=no +RemainAfterExit=no +GuessMainPID=yes +MainPID=0 +ControlPID=0 +FileDescriptorStoreMax=0 +NFileDescriptorStore=0 +StatusErrno=0 +Result=success +UID=[not set] +GID=[not set] +NRestarts=0 +ExecMainStartTimestampMonotonic=0 +ExecMainExitTimestampMonotonic=0 +ExecMainPID=0 +ExecMainCode=0 +ExecMainStatus=0 +ExecStart={ path=/usr/sbin/in.tftpd ; argv[]=/usr/sbin/in.tftpd -u $TFTP_USER -s $TFTP_DIRECTORY $TFTP_OPTIONS ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 } +Slice=system.slice +MemoryCurrent=[not set] +CPUUsageNSec=[not set] +TasksCurrent=[not set] +IPIngressBytes=18446744073709551615 +IPIngressPackets=18446744073709551615 +IPEgressBytes=18446744073709551615 +IPEgressPackets=18446744073709551615 +Delegate=no +CPUAccounting=no +CPUWeight=[not set] +StartupCPUWeight=[not set] +CPUShares=[not set] +StartupCPUShares=[not set] +CPUQuotaPerSecUSec=infinity +IOAccounting=no +IOWeight=[not set] +StartupIOWeight=[not set] +BlockIOAccounting=no +BlockIOWeight=[not set] +StartupBlockIOWeight=[not set] +MemoryAccounting=no +MemoryLow=0 +MemoryHigh=infinity +MemoryMax=infinity +MemorySwapMax=infinity +MemoryLimit=infinity +DevicePolicy=closed +TasksAccounting=yes +TasksMax=4915 +IPAccounting=no +EnvironmentFile=/etc/sysconfig/tftp (ignore_errors=no) +UMask=0022 +LimitCPU=infinity +LimitCPUSoft=infinity +LimitFSIZE=infinity +LimitFSIZESoft=infinity +LimitDATA=infinity +LimitDATASoft=infinity +LimitSTACK=infinity +LimitSTACKSoft=8388608 +LimitCORE=infinity +LimitCORESoft=infinity +LimitRSS=infinity +LimitRSSSoft=infinity +LimitNOFILE=4096 +LimitNOFILESoft=1024 +LimitAS=infinity +LimitASSoft=infinity +LimitNPROC=63322 +LimitNPROCSoft=63322 +LimitMEMLOCK=16777216 +LimitMEMLOCKSoft=16777216 +LimitLOCKS=infinity +LimitLOCKSSoft=infinity +LimitSIGPENDING=63322 +LimitSIGPENDINGSoft=63322 +LimitMSGQUEUE=819200 +LimitMSGQUEUESoft=819200 +LimitNICE=0 +LimitNICESoft=0 +LimitRTPRIO=0 +LimitRTPRIOSoft=0 +LimitRTTIME=infinity +LimitRTTIMESoft=infinity +OOMScoreAdjust=0 +Nice=0 +IOSchedulingClass=0 +IOSchedulingPriority=0 +CPUSchedulingPolicy=0 +CPUSchedulingPriority=0 +TimerSlackNSec=50000 +CPUSchedulingResetOnFork=no +NonBlocking=no +StandardInput=socket +StandardInputData= +StandardOutput=inherit +StandardError=inherit +TTYReset=no +TTYVHangup=no +TTYVTDisallocate=no +SyslogPriority=30 +SyslogLevelPrefix=yes +SyslogLevel=6 +SyslogFacility=3 +LogLevelMax=-1 +SecureBits=0 +CapabilityBoundingSet=cap_chown cap_dac_override cap_dac_read_search cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock cap_ipc_owner cap_sys_module cap_sys_chroot cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config cap_lease cap_audit_write cap_audit_control cap_setfcap cap_mac_override cap_mac_admin cap_syslog cap_wake_alarm cap_block_suspend +AmbientCapabilities= +DynamicUser=no +RemoveIPC=no +MountFlags= +PrivateTmp=no +PrivateDevices=yes +ProtectKernelTunables=no +ProtectKernelModules=no +ProtectControlGroups=no +PrivateNetwork=no +PrivateUsers=no +ProtectHome=no +ProtectSystem=no +SameProcessGroup=no +UtmpMode=init +IgnoreSIGPIPE=yes +NoNewPrivileges=no +SystemCallErrorNumber=0 +LockPersonality=no +RuntimeDirectoryPreserve=no +RuntimeDirectoryMode=0755 +StateDirectoryMode=0755 +CacheDirectoryMode=0755 +LogsDirectoryMode=0755 +ConfigurationDirectoryMode=0755 +MemoryDenyWriteExecute=no +RestrictRealtime=no +RestrictNamespaces=no +MountAPIVFS=no +KeyringMode=private +KillMode=control-group +KillSignal=15 +SendSIGKILL=yes +SendSIGHUP=no +Id=tftp.service +Names=tftp.service +Requires=sysinit.target system.slice +Wants=network.target +Conflicts=shutdown.target +Before=shutdown.target +After=basic.target network.target sysinit.target system.slice +Description=Tftp Server +LoadState=loaded +ActiveState=inactive +SubState=dead +FragmentPath=/usr/lib/systemd/system/tftp.service +UnitFileState=static +UnitFilePreset=disabled +StateChangeTimestampMonotonic=0 +InactiveExitTimestampMonotonic=0 +ActiveEnterTimestampMonotonic=0 +ActiveExitTimestampMonotonic=0 +InactiveEnterTimestampMonotonic=0 +CanStart=yes +CanStop=yes +CanReload=no +CanIsolate=no +StopWhenUnneeded=no +RefuseManualStart=no +RefuseManualStop=no +AllowIsolate=no +DefaultDependencies=yes +OnFailureJobMode=replace +IgnoreOnIsolate=no +NeedDaemonReload=no +JobTimeoutUSec=infinity +JobRunningTimeoutUSec=infinity +JobTimeoutAction=none +ConditionResult=no +AssertResult=no +ConditionTimestampMonotonic=0 +AssertTimestampMonotonic=0 +Transient=no +Perpetual=no +StartLimitIntervalUSec=10s +StartLimitBurst=5 +StartLimitAction=none +FailureAction=none +SuccessAction=none +CollectMode=inactive diff --git a/library/systemd/test/test_helper.rb b/library/systemd/test/test_helper.rb index 791202e5c..91330da99 100644 --- a/library/systemd/test/test_helper.rb +++ b/library/systemd/test/test_helper.rb @@ -19,7 +19,7 @@ def find_term(content, type, id) content.nested_find do |term| next unless term.is_a?(Yast::Term) && term.value == type - term.params.select { |i| i.is_a?(Yast::Term) && i.params == [id] } + term.params.any? { |i| i.is_a?(Yast::Term) && i.params == [id] } end end diff --git a/library/systemd/test/yast2/service_widget_test.rb b/library/systemd/test/yast2/service_widget_test.rb index 589e691c6..d0c214b1f 100755 --- a/library/systemd/test/yast2/service_widget_test.rb +++ b/library/systemd/test/yast2/service_widget_test.rb @@ -23,17 +23,34 @@ require_relative "../test_helper" require "yast2/service_widget" +require "yast2/system_service" describe Yast2::ServiceWidget do + # @param combobox [Yast::Term] + # @param id [Symbol] + def has_item?(combobox, id) + items = combobox.params.last + items.any? { |i| i.params[0].params == [id] } + end + let(:service) do - double( - "Yast2::SystemService", - currently_active?: true, - start_mode: :on_boot, - current_start_mode: :on_boot - ).as_null_object + instance_double( + Yast2::SystemService, + currently_active?: true, + start_mode: :on_boot, + current_start_mode: :on_boot, + start_modes: [:on_boot, :on_demand, :manual], + support_reload?: true, + support_start_on_boot?: on_boot, + support_start_on_demand?: on_demand, + reset: nil, + action: nil + ) end + let(:on_demand) { true } + let(:on_boot) { true } + subject do described_class.new( service @@ -68,6 +85,47 @@ expect(autostart_selector).to_not be_nil end + + describe "start mode selector" do + let(:autostart_selector) { find_term(subject.content, :ComboBox, :service_widget_autostart) } + let(:items) { autostart_selector.params.last } + + context "when the service supports to be started on demand" do + let(:on_demand) { true } + + it "includes an 'on demand' option" do + expect(has_item?(autostart_selector, :service_widget_autostart_on_demand)).to eq(true) + end + end + + context "when the service does not support to be started on demand" do + let(:on_demand) { false } + + it "does not include an 'on demand' option" do + expect(has_item?(autostart_selector, :service_widget_autostart_on_demand)).to eq(false) + end + end + + context "when the service supports to be started on boot" do + let(:on_boot) { true } + + it "includes an 'on boot' option" do + expect(has_item?(autostart_selector, :service_widget_autostart_on_boot)).to eq(true) + end + end + + context "when the service does not support to be started on boot" do + let(:on_boot) { false } + + it "does not include an 'on boot' option" do + expect(has_item?(autostart_selector, :service_widget_autostart_on_boot)).to eq(false) + end + end + + it "includes a 'manual' option" do + expect(has_item?(autostart_selector, :service_widget_autostart_manual)).to eq(true) + end + end end describe "#refresh" do diff --git a/library/systemd/test/yast2/system_service_test.rb b/library/systemd/test/yast2/system_service_test.rb index 348e7ca07..ef0880414 100755 --- a/library/systemd/test/yast2/system_service_test.rb +++ b/library/systemd/test/yast2/system_service_test.rb @@ -369,6 +369,24 @@ end end + describe "#support_start_on_boot?" do + context "when the service is not static" do + let(:service_static) { false } + + it "returns true" do + expect(system_service.support_start_on_boot?).to eq(true) + end + end + + context "when the service is static" do + let(:service_static) { true } + + it "returns false" do + expect(system_service.support_start_on_boot?).to eq(false) + end + end + end + describe "#active?" do context "when no action has been performed over the system service" do context "and the underlying service is active" do diff --git a/library/systemd/test/yast2/systemd_socket_finder_test.rb b/library/systemd/test/yast2/systemd_socket_finder_test.rb old mode 100644 new mode 100755 diff --git a/library/systemd/test/yast2/unit_properties_test.rb b/library/systemd/test/yast2/unit_properties_test.rb new file mode 100755 index 000000000..83e8633ba --- /dev/null +++ b/library/systemd/test/yast2/unit_properties_test.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2018] 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_relative "../test_helper" + +describe Yast2::Systemd::UnitProperties do + include SystemdServiceStubs + + describe "#static?" do + subject(:properties) { described_class.new(service, nil) } + let(:service) { Yast2::Systemd::Service.build(service_name) } + + before do + stub_services(service: service_name) + end + + context "when service is static" do + let(:service_name) { "tftp" } + + it "returns true" do + expect(properties.static?).to eq(true) + end + end + + context "when service is not static" do + let(:service_name) { "sshd" } + + it "returns false" do + expect(properties.static?).to eq(false) + end + end + end +end diff --git a/package/yast2.changes b/package/yast2.changes index a925af4da..748612fe7 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Aug 24 13:36:42 UTC 2018 - igonzalezsosa@suse.com + +- Fix the Yast2::ServiceWidget to not show the "On Boot" option + when it is not supported (related to fate#319428). +- 4.1.4 + ------------------------------------------------------------------- Tue Aug 21 14:15:07 UTC 2018 - dgonzalez@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 3df59fc87..d47d478d0 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.3 +Version: 4.1.4 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From ee1850d7f97e02c1767f11c7dba51269a7f290a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= <1691872+dgdavid@users.noreply.github.com> Date: Thu, 30 Aug 2018 12:45:34 +0100 Subject: [PATCH 094/223] Increase timeout for the execution of systemctl commands (#808) * Increase the timeout for systemctl command execution > Since systemd by itself has a 30 sec timeout, YaST should add some more > seconds to allow for systemctl to exit gracefully even if it has a > timeout and not just kill the systemctl command just one seconds before > it would time out itself. See https://bugzilla.suse.com/show_bug.cgi?id=1098910#c3 * Update version and changelog --- library/systemd/src/lib/yast2/systemctl.rb | 2 +- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/library/systemd/src/lib/yast2/systemctl.rb b/library/systemd/src/lib/yast2/systemctl.rb index 5f3eba610..ee031b4f2 100644 --- a/library/systemd/src/lib/yast2/systemctl.rb +++ b/library/systemd/src/lib/yast2/systemctl.rb @@ -20,7 +20,7 @@ module Systemctl COMMAND_OPTIONS = " --no-legend --no-pager --no-ask-password ".freeze ENV_VARS = " LANG=C TERM=dumb COLUMNS=1024 ".freeze SYSTEMCTL = ENV_VARS + CONTROL + COMMAND_OPTIONS - TIMEOUT = 30 # seconds + TIMEOUT = 40 # seconds class << self BASH_SCR_PATH = Yast::Path.new(".target.bash_output") diff --git a/package/yast2.changes b/package/yast2.changes index 4ff9059d1..1a076be6c 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Thu Aug 30 11:21:14 UTC 2018 - David Díaz + +- Increase timeout for the execution of systemctl commands + (bsc#1098910). +- 4.0.86 + ------------------------------------------------------------------- Tue Aug 14 17:32:02 UTC 2018 - igonzalezsosa@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 001e8521d..3c7eced58 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.85 +Version: 4.0.86 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From b87d01b8150634799ecec5137006e4bc47583c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= <1691872+dgdavid@users.noreply.github.com> Date: Thu, 30 Aug 2018 13:18:09 +0100 Subject: [PATCH 095/223] Increase timeout for the execution of systemctl commands (#808) (#809) * Increase the timeout for systemctl command execution > Since systemd by itself has a 30 sec timeout, YaST should add some more > seconds to allow for systemctl to exit gracefully even if it has a > timeout and not just kill the systemctl command just one seconds before > it would time out itself. See https://bugzilla.suse.com/show_bug.cgi?id=1098910#c3 * Update version and changelog --- library/systemd/src/lib/yast2/systemctl.rb | 2 +- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/library/systemd/src/lib/yast2/systemctl.rb b/library/systemd/src/lib/yast2/systemctl.rb index e33b67fb7..95bc060c2 100644 --- a/library/systemd/src/lib/yast2/systemctl.rb +++ b/library/systemd/src/lib/yast2/systemctl.rb @@ -21,7 +21,7 @@ def initialize(details) COMMAND_OPTIONS = " --no-legend --no-pager --no-ask-password ".freeze ENV_VARS = " LANG=C TERM=dumb COLUMNS=1024 ".freeze SYSTEMCTL = ENV_VARS + CONTROL + COMMAND_OPTIONS - TIMEOUT = 30 # seconds + TIMEOUT = 40 # seconds class << self BASH_SCR_PATH = Yast::Path.new(".target.bash_output") diff --git a/package/yast2.changes b/package/yast2.changes index 748612fe7..1e9acc6bc 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Thu Aug 30 11:21:14 UTC 2018 - David Díaz + +- Increase timeout for the execution of systemctl commands + (bsc#1098910). +- 4.1.5 + ------------------------------------------------------------------- Fri Aug 24 13:36:42 UTC 2018 - igonzalezsosa@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index d47d478d0..806f69034 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.4 +Version: 4.1.5 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 6c101dacb1133ccb008c92ddceb6d5ab1c11532e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20D=C3=ADaz?= <1691872+dgdavid@users.noreply.github.com> Date: Thu, 30 Aug 2018 14:34:41 +0100 Subject: [PATCH 096/223] Fix changelog (#810) Do not include the author's name --- package/yast2.changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 1a076be6c..5788fdc50 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,5 +1,5 @@ ------------------------------------------------------------------- -Thu Aug 30 11:21:14 UTC 2018 - David Díaz +Thu Aug 30 11:21:14 UTC 2018 - dgonzalez@suse.com - Increase timeout for the execution of systemctl commands (bsc#1098910). From 57a4e12078b8c8fcbeddf65c831dd5b606cf20b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 30 Aug 2018 14:56:24 +0100 Subject: [PATCH 097/223] Add support start on boot (#812) * Add missing support_start_on_boot? method * Bump version and update changes file --- .../systemd/src/lib/yast2/compound_service.rb | 9 ++++ .../test/yast2/compound_service_test.rb | 42 +++++++++++++++++-- package/yast2.changes | 7 ++++ package/yast2.spec | 2 +- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 60d3b8ab4..0e03e23b2 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -201,6 +201,15 @@ def support_start_on_demand? services.any?(&:support_start_on_demand?) end + # Whether the service supports :on_boot start mode + # + # @see Yast2::SystemService#support_start_on_boot? + # + # @return [Boolean] + def support_start_on_boot? + services.any?(&:support_start_on_boot?) + end + # Errors when trying to write changes to the underlying system # # @see Yast2::SystemService#errors diff --git a/library/systemd/test/yast2/compound_service_test.rb b/library/systemd/test/yast2/compound_service_test.rb index 90013b42c..f1e6e3ca2 100755 --- a/library/systemd/test/yast2/compound_service_test.rb +++ b/library/systemd/test/yast2/compound_service_test.rb @@ -292,10 +292,46 @@ def service(*args) end describe "#support_start_on_demand?" do - subject { described_class.new(service(support_start_on_demand?: true), service(support_start_on_demand?: false)) } + let(:service1) { service(support_start_on_demand?: false) } - it "returns true if any service supports starting on demand" do - expect(subject.support_start_on_demand?).to eq true + subject { described_class.new(service1, service2) } + + context "when any service supports starting on demand" do + let(:service2) { service(support_start_on_demand?: true) } + + it "returns true" do + expect(subject.support_start_on_demand?).to eq true + end + end + + context "when no service support starting on demand" do + let(:service2) { service(support_start_on_demand?: false) } + + it "returns false" do + expect(subject.support_start_on_demand?).to eq false + end + end + end + + describe "#support_start_on_boot?" do + let(:service1) { service(support_start_on_boot?: false) } + + subject { described_class.new(service1, service2) } + + context "when any service supports starting on boot" do + let(:service2) { service(support_start_on_boot?: true) } + + it "returns true" do + expect(subject.support_start_on_boot?).to eq true + end + end + + context "when no service support starting on boot" do + let(:service2) { service(support_start_on_boot?: false) } + + it "returns false" do + expect(subject.support_start_on_boot?).to eq false + end end end diff --git a/package/yast2.changes b/package/yast2.changes index ff9723d0f..df3109760 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Thu Aug 30 13:39:02 UTC 2018 - igonzalezsosa@suse.com + +- Add missing CompoundService#support_start_on_boot? + (bsc#1106591). +- 4.1.6 + ------------------------------------------------------------------- Thu Aug 30 11:21:14 UTC 2018 - dgonzalez@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 806f69034..12feda762 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.5 +Version: 4.1.6 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 37446fddd122d9ee8185b83010d6b507b46dc14b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 4 Sep 2018 14:49:44 +0100 Subject: [PATCH 098/223] System services documentation (#814) * Documentation fixes * Fix SystemService documentation * Add an overview of the system services API * Fix services and sockets documentation formatting * Reduce services and sockets nesting --- .yardopts | 2 +- library/system/doc/system_services.md | 36 +++++++++++++ library/systemd/doc/services_and_sockets.md | 54 ++++++++++--------- .../systemd/src/lib/yast2/compound_service.rb | 10 ++-- .../systemd/src/lib/yast2/system_service.rb | 2 +- .../src/lib/yast2/systemd/unit_properties.rb | 2 +- 6 files changed, 74 insertions(+), 32 deletions(-) create mode 100644 library/system/doc/system_services.md diff --git a/.yardopts b/.yardopts index ef4b67d51..0a3e27590 100644 --- a/.yardopts +++ b/.yardopts @@ -1 +1 @@ ---no-private --markup markdown --protected library/*/src/**/*.rb --readme README.md --output-dir ./doc/autodocs - library/cwm/doc/CWM.md library/systemd/doc/services_and_sockets.md +--no-private --markup markdown --protected library/*/src/**/*.rb --readme README.md --output-dir ./doc/autodocs - library/cwm/doc/CWM.md library/systemd/doc/services_and_sockets.md library/system/doc/system_services.md diff --git a/library/system/doc/system_services.md b/library/system/doc/system_services.md new file mode 100644 index 000000000..42ed6c8fc --- /dev/null +++ b/library/system/doc/system_services.md @@ -0,0 +1,36 @@ +# System Services Library + +The system services library offers an API to interact with system services, allowing the user to +perform typical operations like querying the services, starting or stopping them, etc. + +The set of classes which are included in this library can be divided into: + +* A high level API which offers some abstractions on top of Systemd. +* A low level one to talk closely to Systemd units (including services and sockets). + +Additionally, a widget that can be used in YaST modules (like yast2-dns-server) is included. + +## High Level API + +The high level API is composed by these classes: + +* {Yast2::SystemService}: represents a service (like `cups` or `dbus`) from a high level point of + view. Systemd concepts like *units* or *sockets* are abstracted by this class. +* {Yast2::CompoundService}: groups a set of related services that might be handled together. + Think, for instance, about `iscsi`, `iscsid` and `iscsiuio` services in `yast2-iscsi-client`. + This class offers basically the same API than {Yast2::SystemService}. + +## Low Level API + +The low level API can be more convenient in some situations and it is basically composed of a set of +classes that map to Systemd concepts: {Yast2::Systemd::Unit}, {Yast2::Systemd::Service}, +{Yast2::Systemd::Socket} and {Yast2::Systemd::Target}. + +## Service Widget + +Additionally to the classes to interact with system services, this library offers a widget which +allows the user to decide how (and when) a service should be started. It is meant to be used by +modules which configure a given service (like `yast2-dns-server` or `yast2-iscsi-client`). + +See {Yast2::ServiceWidget} for the widget documentation and {CWM::ServiceWidget} for the CWM +wrapper. diff --git a/library/systemd/doc/services_and_sockets.md b/library/systemd/doc/services_and_sockets.md index cce2c6555..4e7b7125a 100644 --- a/library/systemd/doc/services_and_sockets.md +++ b/library/systemd/doc/services_and_sockets.md @@ -1,35 +1,45 @@ # Services and Sockets -In systemd, it might happen that a single service is managed by a set of units, for example when a service is able to be activated via socket, path, timer, etc. In such cases, systemd works with a service unit (e.g., cups.service) and also with a socket (or path, timer, etc) unit (e.g., cups.socket). +In systemd, it might happen that a single service is managed by a set of units, for example when a +service is able to be activated via socket, path, timer, etc. In such cases, systemd works with a +service unit (e.g., cups.service) and also with a socket (or path, timer, etc) unit (e.g., +cups.socket). -When a service is configured by using YaST (e.g., with the Services Manager), all units related to each service must be taken into account. For example, when a service is stopped, the socket associated to such service should be also stopped. Otherwise, the sevice could be automatically activated again via its socket. +When a service is configured by using YaST (e.g., with the Services Manager), all units related to +each service must be taken into account. For example, when a service is stopped, the socket +associated to such service should be also stopped. Otherwise, the sevice could be automatically +activated again via its socket. -This file describes a new class (named {Yast2::SystemService}) to work with the service and its associated socket as a whole. The main goal of this new class is to perform actions over both, the service and the socket, when it is required. +This file extends the {Yast2::SystemService} class documentation describing how it works with the +service and its associated socket as a whole. The main goal of this new class is to perform actions +over both, the service and the socket, when it is required. ## {Yast2::SystemService} class -This class is intended to represent a service from a high level point of view. It will offer an API to: +This class is intended to represent a service from a high level point of view. It will offer an API +to: * ask for service properties (e.g., is active, its state, start mode, etc) -* perform "in memory" actions (start, stop, restart or reload) and change its start mode (on boot, on demand or manually) +* perform "in memory" actions (start, stop, restart or reload) and change its start mode (on boot, + on demand or manually) * apply all changes in the "real system" -One goal in this class is to offer an agnostic API. At this moment it uses Systemd in low levels layers, but in future this could change and the API should remain as much as possible. +One goal in this class is to offer an agnostic API. At this moment it uses Systemd in low levels +layers, but in future this could change and the API should remain as much as possible. -### Actions over Systemd units +## Actions over Systemd units -The following table describes the actions to perform over a systemd service and its associated socket (if any) when we try to start, stop, restart or reload a `SystemService`. +The following table describes the actions to perform over a systemd service and its associated +socket (if any) when we try to start, stop, restart or reload a `SystemService`. +| Status | Start | Stop | Restart | Reload | +| --- | --- | --- | --- | --- | +| Only socket running | nothing | stop socket | stop socket and start service/socket (depending on start mode) | nothing | +| Only service running | nothing | stop service | stop service and start service/socket (depending on start mode) | reload service (if it support, otherwise restart) | +| Socket & Service running | nothing | stop service and socket | stop service and socket and start service/socket (depending on start mode) | reload service (if it support, otherwise restart) | +| Nothing runs | start service/socket (depending on start mode) | nothing | start service/socket (depending on start mode) | start service/socket (depending on start mode) | -| Status | Start | Stop | Restart | Reload | -|---|---|---|---|---| -| Only socket running | nothing | stop socket | stop socket and start service/socket (depending on start mode) | nothing | -| Only service running | nothing | stop service | stop service and start service/socket (depending on start mode) | reload service (if it support, otherwise restart) | -| Socket & Service running | nothing | stop service and socket | stop service and socket and start service/socket (depending on start mode) | reload service (if it support, otherwise restart) | -| Nothing runs | start service/socket (depending on start mode) | nothing | start service/socket (depending on start mode) | start service/socket (depending on start mode) | - - -### Detailed actions +## Detailed actions Here each `SystemService` action is decribed in a more detailed way. @@ -52,8 +62,7 @@ First of all, we are going to consider that a `SystemService` is stopped/running ### `#stop` -* Goal - * service is stopped (see [1]) +* Goal: service is stopped (see [1]) * Actions * stops systemd socket if it is running @@ -61,8 +70,7 @@ First of all, we are going to consider that a `SystemService` is stopped/running ### `#restart` -* Goal - * service is stopped (see [1]) and started again (see [2]) +* Goal: service is stopped (see [1]) and started again (see [2]) * Actions * calls to "stop service action" (see stop section) if the service is running (see [2]) @@ -70,9 +78,7 @@ First of all, we are going to consider that a `SystemService` is stopped/running ### `#reload` -* Goal - * service is reloaded or restarted or started - +* Goal: service is reloaded or restarted or started * Actions * when the service supports reload * and start mode is `on demand` diff --git a/library/systemd/src/lib/yast2/compound_service.rb b/library/systemd/src/lib/yast2/compound_service.rb index 0e03e23b2..f6c6637fd 100644 --- a/library/systemd/src/lib/yast2/compound_service.rb +++ b/library/systemd/src/lib/yast2/compound_service.rb @@ -51,7 +51,7 @@ def initialize(*services) # Saves all services # - # @see {Yast2::SystemService#save} + # @see Yast2::SystemService#save # # @return [Boolean] true if all services were correctly saved; false otherwise def save(keep_state: false) @@ -79,7 +79,7 @@ def currently_active? # Whether any service supports reload # - # @see {Yast2::SystemService#support_reload?} + # @see Yast2::SystemService#support_reload? # # @return [Boolean] def support_reload? @@ -92,7 +92,7 @@ def support_reload? # service supports :on_demand too, the possible start modes will the # all of them: :on_boot, on_demand and :manual. # - # @see {Yast2::SystemService#start_modes} + # @see Yast2::SystemService#start_modes # # @return [Array] start modes (:on_boot, :on_demand, :manual) def start_modes @@ -101,7 +101,7 @@ def start_modes # Keywords that can be used to search for involved underlying units # - # @see {Yast2::SystemService#keywords} + # @see Yast2::SystemService#keywords # # @return [Array] def keywords @@ -176,7 +176,7 @@ def start_mode # # @note It offers and additional start mode `:inconsistent` that is not in {Yast2::SystemService} # - # @param [Symbol] new start mode (e.g., :on_boot, :on_demand, :manual, :inconsistent) + # @param configuration [Symbol] new start mode (e.g., :on_boot, :on_demand, :manual, :inconsistent) def start_mode=(configuration) if !AUTOSTART_OPTIONS.include?(configuration) raise ArgumentError, "Invalid parameter #{configuration.inspect}" diff --git a/library/systemd/src/lib/yast2/system_service.rb b/library/systemd/src/lib/yast2/system_service.rb index b049003dc..a8c28ded1 100644 --- a/library/systemd/src/lib/yast2/system_service.rb +++ b/library/systemd/src/lib/yast2/system_service.rb @@ -32,7 +32,7 @@ module Yast2 # See also {file:library/systemd/doc/services_and_sockets.md}. # # @note All changes performed over an object of this class are not applied into the - # underlying system until the {#save} method is called. + # underlying system until the {#save} method is called. # # @example Enabling a service # cups = SystemService.find("cups") diff --git a/library/systemd/src/lib/yast2/systemd/unit_properties.rb b/library/systemd/src/lib/yast2/systemd/unit_properties.rb index ff9560a0d..7ea9edbc4 100644 --- a/library/systemd/src/lib/yast2/systemd/unit_properties.rb +++ b/library/systemd/src/lib/yast2/systemd/unit_properties.rb @@ -8,7 +8,7 @@ class UnitProperties < OpenStruct SUPPORTED_STATES = %w(enabled disabled).freeze - # Values of {#active_state} fow which we consider a unit "active". + # Values of `#active_state` fow which we consider a unit "active". # # systemctl.c:check_unit_active uses (active, reloading) # For bsc#884756 we also consider "activating" to be active. From 4d494a0e471f5ee248de8cefe67f7aab1ca21c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 5 Sep 2018 11:15:36 +0100 Subject: [PATCH 099/223] Add option next_handler to CWM#show --- library/cwm/examples/object_api.rb | 2 ++ library/cwm/src/modules/CWM.rb | 56 ++++++++++++------------------ 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/library/cwm/examples/object_api.rb b/library/cwm/examples/object_api.rb index c2e49ace0..f8ced551d 100644 --- a/library/cwm/examples/object_api.rb +++ b/library/cwm/examples/object_api.rb @@ -58,10 +58,12 @@ def run ) Yast::Wizard.CreateDialog + next_handler = proc { Yast::Popup.YesNo("Really go next?") } back_handler = proc { Yast::Popup.YesNo("Really go back?") } abort_handler = proc { Yast::Popup.YesNo("Really abort?") } CWM.show(contents, caption: _("Lucky number"), + next_handler: next_handler, back_handler: back_handler, abort_handler: abort_handler) Yast::Wizard.CloseDialog diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index de165c53d..b5181f9c8 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -834,31 +834,16 @@ def Run(widgets, functions, skip_store_for: []) end end + ret = :next if ret == :ok ret = :abort if ret == :cancel - if ret == :abort - if Ops.get(functions, :abort) - toEval = Convert.convert( - Ops.get(functions, :abort), - from: "any", - to: "boolean ()" - ) - if !toEval.nil? - eval_ret = toEval.call - ret = eval_ret ? :abort : nil - end - end - elsif ret == :back - if Ops.get(functions, :back) - toEval = Convert.convert( - Ops.get(functions, :back), - from: "any", - to: "boolean ()" - ) - if !toEval.nil? - eval_ret = toEval.call - ret = eval_ret ? :back : nil - end - end + + if ret == :next && functions[:next] + ret = nil unless functions[:next].call + save = false if ret.nil? + elsif ret == :back && functions[:back] + ret = nil unless functions[:back].call + elsif ret == :abort && functions[:abort] + ret = nil unless functions[:abort].call end next if ret.nil? @@ -930,27 +915,31 @@ def SetValidationFailedHandler(handler) end # Display the dialog and run its event loop using new widget API - # @param [::CWM::WidgetTerm] contents is UI term including instances of {CWM::AbstractWidget} - # @param [String] caption of dialog - # @param [String, nil] back_button label for dialog back button, + # @param contents [::CWM::WidgetTerm] UI term including instances of {CWM::AbstractWidget} + # @param caption [String] caption of dialog + # @param back_button [String, nil] label for dialog back button, # `nil` to use the default label, `""` to omit the button - # @param [String, nil] next_button label for dialog next button, + # @param next_button [String, nil] label for dialog next button, # `nil` to use the default label, `""` to omit the button - # @param [String, nil] abort_button label for dialog abort button, + # @param abort_button [String, nil] label for dialog abort button, # `nil` to use the default label, `""` to omit the button - # @param [Array] skip_store_for list of events for which the value of the widget will not be stored. + # @param skip_store_for [Array] list of events for which the value of the widget will not be stored. # Useful mainly when some widget returns an event that should not trigger the storing, # like a reset button or a redrawing. It will skip also validation, because it is not needed # as nothing is stored. - # @param [Proc] back_handler handler that is called after clicking on back. If it returns false, + # @param next_handler [Proc] handler that is called after clicking on next. If it returns false, + # then it does not go next. If it returns true, then :next symbol is returned. If handler is not + # defined, then it acts like if it returns true. + # @param back_handler [Proc] handler that is called after clicking on back. If it returns false, # then it does not go back. If it returns true, then :back symbol is returned. If handler is not # defined, then it acts like if it returns true. - # @param [Proc] abort_handler handler that is called after clicking on abort. If it returns false, + # @param abort_handler [Proc] handler that is called after clicking on abort. If it returns false, # then it stops abort. If it returns true, then :abort symbol is returned. If handler is not # defined, then it acts like if it returns true. + # # @return [Symbol] wizard sequencer symbol def show(contents, caption: nil, back_button: nil, next_button: nil, abort_button: nil, skip_store_for: [], - disable_buttons: [], back_handler: nil, abort_handler: nil) + disable_buttons: [], next_handler: nil, back_handler: nil, abort_handler: nil) widgets = widgets_in_contents(contents) options = { "contents" => widgets_contents(contents), @@ -964,6 +953,7 @@ def show(contents, caption: nil, back_button: nil, next_button: nil, abort_butto options["skip_store_for"] = skip_store_for options["disable_buttons"] = disable_buttons options["fallback_functions"] = {} + options["fallback_functions"][:next] = Yast.fun_ref(next_handler, "boolean ()") if next_handler options["fallback_functions"][:back] = Yast.fun_ref(back_handler, "boolean ()") if back_handler options["fallback_functions"][:abort] = Yast.fun_ref(abort_handler, "boolean ()") if abort_handler From 5d6a1cf79a070438858ee16b44b0fbfeb878806e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 5 Sep 2018 11:16:19 +0100 Subject: [PATCH 100/223] Add default next handler to CWM::Dialog --- library/cwm/src/lib/cwm/dialog.rb | 10 ++++++++++ library/cwm/test/dialog_test.rb | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/library/cwm/src/lib/cwm/dialog.rb b/library/cwm/src/lib/cwm/dialog.rb index b37418271..22ac8f897 100644 --- a/library/cwm/src/lib/cwm/dialog.rb +++ b/library/cwm/src/lib/cwm/dialog.rb @@ -76,6 +76,15 @@ def disable_buttons [] end + # Handler when the next button is used + # + # If returns false, then it does not go next. + # + # @return [Boolean] + def next_handler + true + end + # Handler when the back button is used # # If returns false, then it does not go back. @@ -116,6 +125,7 @@ def cwm_show next_button: next_button, skip_store_for: skip_store_for, disable_buttons: disable_buttons, + next_handler: proc { next_handler }, back_handler: proc { back_handler }, abort_handler: proc { abort_handler } ) diff --git a/library/cwm/test/dialog_test.rb b/library/cwm/test/dialog_test.rb index 6d90d1b38..17d7a35f4 100755 --- a/library/cwm/test/dialog_test.rb +++ b/library/cwm/test/dialog_test.rb @@ -39,6 +39,16 @@ def contents expect(subject.class.run).to eq(:launch) end + it "passes the next handler to CWM#show" do + expect(Yast::CWM).to receive(:show) do |_content, options| + expect(options).to include(:next_handler) + # Checking the default handler is passed (simply returns true) + expect(options[:next_handler].call).to eq(true) + end + + subject.class.run + end + it "passes the back handler to CWM#show" do expect(Yast::CWM).to receive(:show) do |_content, options| expect(options).to include(:back_handler) From 7a44eba7314549e3185b8af395cecb1be533feed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 5 Sep 2018 11:21:53 +0100 Subject: [PATCH 101/223] Update version and changelog --- package/yast2.changes | 8 ++++++++ package/yast2.spec | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index df3109760..a56becf35 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Wed Sep 5 10:19:27 UTC 2018 - jlopez@suse.com + +- CWM: allow to define next handler for CWM#show. +- CWM: define default next handler in CWM::Dialog. +- Needed for Expert Partitioner (fate##318196). +- 4.1.7 + ------------------------------------------------------------------- Thu Aug 30 13:39:02 UTC 2018 - igonzalezsosa@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 12feda762..2aa41bfc9 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.6 +Version: 4.1.7 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From c8fadf5e2dffc2256cc82cfff354719c2f0d8ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Fri, 7 Sep 2018 09:38:39 +0100 Subject: [PATCH 102/223] Fix fate --- package/yast2.changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index a56becf35..0f55a65a2 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -3,7 +3,7 @@ Wed Sep 5 10:19:27 UTC 2018 - jlopez@suse.com - CWM: allow to define next handler for CWM#show. - CWM: define default next handler in CWM::Dialog. -- Needed for Expert Partitioner (fate##318196). +- Needed for Expert Partitioner (fate#318196). - 4.1.7 ------------------------------------------------------------------- From b931402ab663f2b0abfb642e2abef29c3c739de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 5 Sep 2018 11:15:36 +0100 Subject: [PATCH 103/223] Add option next_handler to CWM#show --- library/cwm/examples/object_api.rb | 2 ++ library/cwm/src/modules/CWM.rb | 56 ++++++++++++------------------ 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/library/cwm/examples/object_api.rb b/library/cwm/examples/object_api.rb index c2e49ace0..f8ced551d 100644 --- a/library/cwm/examples/object_api.rb +++ b/library/cwm/examples/object_api.rb @@ -58,10 +58,12 @@ def run ) Yast::Wizard.CreateDialog + next_handler = proc { Yast::Popup.YesNo("Really go next?") } back_handler = proc { Yast::Popup.YesNo("Really go back?") } abort_handler = proc { Yast::Popup.YesNo("Really abort?") } CWM.show(contents, caption: _("Lucky number"), + next_handler: next_handler, back_handler: back_handler, abort_handler: abort_handler) Yast::Wizard.CloseDialog diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index de165c53d..b5181f9c8 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -834,31 +834,16 @@ def Run(widgets, functions, skip_store_for: []) end end + ret = :next if ret == :ok ret = :abort if ret == :cancel - if ret == :abort - if Ops.get(functions, :abort) - toEval = Convert.convert( - Ops.get(functions, :abort), - from: "any", - to: "boolean ()" - ) - if !toEval.nil? - eval_ret = toEval.call - ret = eval_ret ? :abort : nil - end - end - elsif ret == :back - if Ops.get(functions, :back) - toEval = Convert.convert( - Ops.get(functions, :back), - from: "any", - to: "boolean ()" - ) - if !toEval.nil? - eval_ret = toEval.call - ret = eval_ret ? :back : nil - end - end + + if ret == :next && functions[:next] + ret = nil unless functions[:next].call + save = false if ret.nil? + elsif ret == :back && functions[:back] + ret = nil unless functions[:back].call + elsif ret == :abort && functions[:abort] + ret = nil unless functions[:abort].call end next if ret.nil? @@ -930,27 +915,31 @@ def SetValidationFailedHandler(handler) end # Display the dialog and run its event loop using new widget API - # @param [::CWM::WidgetTerm] contents is UI term including instances of {CWM::AbstractWidget} - # @param [String] caption of dialog - # @param [String, nil] back_button label for dialog back button, + # @param contents [::CWM::WidgetTerm] UI term including instances of {CWM::AbstractWidget} + # @param caption [String] caption of dialog + # @param back_button [String, nil] label for dialog back button, # `nil` to use the default label, `""` to omit the button - # @param [String, nil] next_button label for dialog next button, + # @param next_button [String, nil] label for dialog next button, # `nil` to use the default label, `""` to omit the button - # @param [String, nil] abort_button label for dialog abort button, + # @param abort_button [String, nil] label for dialog abort button, # `nil` to use the default label, `""` to omit the button - # @param [Array] skip_store_for list of events for which the value of the widget will not be stored. + # @param skip_store_for [Array] list of events for which the value of the widget will not be stored. # Useful mainly when some widget returns an event that should not trigger the storing, # like a reset button or a redrawing. It will skip also validation, because it is not needed # as nothing is stored. - # @param [Proc] back_handler handler that is called after clicking on back. If it returns false, + # @param next_handler [Proc] handler that is called after clicking on next. If it returns false, + # then it does not go next. If it returns true, then :next symbol is returned. If handler is not + # defined, then it acts like if it returns true. + # @param back_handler [Proc] handler that is called after clicking on back. If it returns false, # then it does not go back. If it returns true, then :back symbol is returned. If handler is not # defined, then it acts like if it returns true. - # @param [Proc] abort_handler handler that is called after clicking on abort. If it returns false, + # @param abort_handler [Proc] handler that is called after clicking on abort. If it returns false, # then it stops abort. If it returns true, then :abort symbol is returned. If handler is not # defined, then it acts like if it returns true. + # # @return [Symbol] wizard sequencer symbol def show(contents, caption: nil, back_button: nil, next_button: nil, abort_button: nil, skip_store_for: [], - disable_buttons: [], back_handler: nil, abort_handler: nil) + disable_buttons: [], next_handler: nil, back_handler: nil, abort_handler: nil) widgets = widgets_in_contents(contents) options = { "contents" => widgets_contents(contents), @@ -964,6 +953,7 @@ def show(contents, caption: nil, back_button: nil, next_button: nil, abort_butto options["skip_store_for"] = skip_store_for options["disable_buttons"] = disable_buttons options["fallback_functions"] = {} + options["fallback_functions"][:next] = Yast.fun_ref(next_handler, "boolean ()") if next_handler options["fallback_functions"][:back] = Yast.fun_ref(back_handler, "boolean ()") if back_handler options["fallback_functions"][:abort] = Yast.fun_ref(abort_handler, "boolean ()") if abort_handler From 3bd3cbcc58ea8af9bf31fb8cb2485c78f298bab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 5 Sep 2018 11:16:19 +0100 Subject: [PATCH 104/223] Add default next handler to CWM::Dialog --- library/cwm/src/lib/cwm/dialog.rb | 10 ++++++++++ library/cwm/test/dialog_test.rb | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/library/cwm/src/lib/cwm/dialog.rb b/library/cwm/src/lib/cwm/dialog.rb index b37418271..22ac8f897 100644 --- a/library/cwm/src/lib/cwm/dialog.rb +++ b/library/cwm/src/lib/cwm/dialog.rb @@ -76,6 +76,15 @@ def disable_buttons [] end + # Handler when the next button is used + # + # If returns false, then it does not go next. + # + # @return [Boolean] + def next_handler + true + end + # Handler when the back button is used # # If returns false, then it does not go back. @@ -116,6 +125,7 @@ def cwm_show next_button: next_button, skip_store_for: skip_store_for, disable_buttons: disable_buttons, + next_handler: proc { next_handler }, back_handler: proc { back_handler }, abort_handler: proc { abort_handler } ) diff --git a/library/cwm/test/dialog_test.rb b/library/cwm/test/dialog_test.rb index 6d90d1b38..17d7a35f4 100755 --- a/library/cwm/test/dialog_test.rb +++ b/library/cwm/test/dialog_test.rb @@ -39,6 +39,16 @@ def contents expect(subject.class.run).to eq(:launch) end + it "passes the next handler to CWM#show" do + expect(Yast::CWM).to receive(:show) do |_content, options| + expect(options).to include(:next_handler) + # Checking the default handler is passed (simply returns true) + expect(options[:next_handler].call).to eq(true) + end + + subject.class.run + end + it "passes the back handler to CWM#show" do expect(Yast::CWM).to receive(:show) do |_content, options| expect(options).to include(:back_handler) From 79dd4add9464e7d0efd52a1ddf811c44c4bdec74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 5 Sep 2018 11:21:53 +0100 Subject: [PATCH 105/223] Update version and changelog --- package/yast2.changes | 8 ++++++++ package/yast2.spec | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 5788fdc50..f00d6b4c3 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Wed Sep 5 10:19:27 UTC 2018 - jlopez@suse.com + +- CWM: allow to define next handler for CWM#show. +- CWM: define default next handler in CWM::Dialog. +- Needed for Expert Partitioner (fate##318196). +- 4.0.87 + ------------------------------------------------------------------- Thu Aug 30 11:21:14 UTC 2018 - dgonzalez@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 3c7eced58..67498e2e3 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.86 +Version: 4.0.87 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From d9d174c0c4299bd7385f570bef4e09ca7c6f76c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Fri, 7 Sep 2018 09:38:39 +0100 Subject: [PATCH 106/223] Fix fate --- package/yast2.changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index f00d6b4c3..6a6752319 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -3,7 +3,7 @@ Wed Sep 5 10:19:27 UTC 2018 - jlopez@suse.com - CWM: allow to define next handler for CWM#show. - CWM: define default next handler in CWM::Dialog. -- Needed for Expert Partitioner (fate##318196). +- Needed for Expert Partitioner (fate#318196). - 4.0.87 ------------------------------------------------------------------- From 5bfc6022636e86539c2e31c46488fe4d0e7c5b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 12 Sep 2018 13:09:19 +0100 Subject: [PATCH 107/223] Extended API supporting sinle-value attributes (#818) * Extended API supporting sinle-value attributes * Removed the deprecated SuSEFirewallProposal. * Use modify_ prefix for firewalld api setters * Added some API services and zones unit tests. * Bump version & changelog. --- library/network/src/Makefile.am | 4 +- .../network/src/lib/y2firewall/firewalld.rb | 85 +- .../src/lib/y2firewall/firewalld/api.rb | 81 +- .../lib/y2firewall/firewalld/api/services.rb | 89 +- .../src/lib/y2firewall/firewalld/api/zones.rb | 223 +++-- .../src/lib/y2firewall/firewalld/relations.rb | 100 ++- .../src/lib/y2firewall/firewalld/service.rb | 24 +- .../y2firewall/firewalld/service_reader.rb | 59 ++ .../src/lib/y2firewall/firewalld/zone.rb | 22 +- .../lib/y2firewall/firewalld/zone_parser.rb | 79 -- .../lib/y2firewall/firewalld/zone_reader.rb | 132 +++ .../src/modules/SuSEFirewallProposal.rb | 772 ------------------ library/network/test/Makefile.am | 1 - .../test/cwm_firewall_interfaces_test.rb | 36 +- .../test/susefirewall_proposal_test.rb | 81 -- .../y2firewall/firewalld/api/services_test.rb | 172 ++++ .../y2firewall/firewalld/api/zones_test.rb | 285 +++++++ .../test/y2firewall/firewalld/api_test.rb | 247 ++++++ .../firewalld/service_reader_test.rb | 78 ++ .../test/y2firewall/firewalld/service_test.rb | 204 +++++ .../{zone_parser.rb => zone_reader_test.rb} | 34 +- .../test/y2firewall/firewalld/zone_test.rb | 109 ++- .../network/test/y2firewall/firewalld_test.rb | 67 +- .../y2firewall/helpers/interfaces_test.rb | 0 .../network/test/y2firewall/relations_test.rb | 96 +++ .../testsuite/tests/NO/SuSEFirewall.err | 0 .../testsuite/tests/NO/SuSEFirewall.out | 84 -- .../testsuite/tests/NO/SuSEFirewall.rb | 556 ------------- .../tests/NO/SuSEFirewallExpertRules.err | 0 .../tests/NO/SuSEFirewallExpertRules.out | 34 - .../tests/NO/SuSEFirewallExpertRules.rb | 142 ---- package/yast2.changes | 12 + package/yast2.spec | 2 +- 33 files changed, 1900 insertions(+), 2010 deletions(-) create mode 100644 library/network/src/lib/y2firewall/firewalld/service_reader.rb delete mode 100644 library/network/src/lib/y2firewall/firewalld/zone_parser.rb create mode 100644 library/network/src/lib/y2firewall/firewalld/zone_reader.rb delete mode 100644 library/network/src/modules/SuSEFirewallProposal.rb delete mode 100755 library/network/test/susefirewall_proposal_test.rb create mode 100755 library/network/test/y2firewall/firewalld/api/services_test.rb create mode 100755 library/network/test/y2firewall/firewalld/api/zones_test.rb create mode 100755 library/network/test/y2firewall/firewalld/api_test.rb create mode 100755 library/network/test/y2firewall/firewalld/service_reader_test.rb create mode 100755 library/network/test/y2firewall/firewalld/service_test.rb rename library/network/test/y2firewall/firewalld/{zone_parser.rb => zone_reader_test.rb} (69%) mode change 100644 => 100755 mode change 100644 => 100755 library/network/test/y2firewall/firewalld/zone_test.rb mode change 100644 => 100755 library/network/test/y2firewall/firewalld_test.rb mode change 100644 => 100755 library/network/test/y2firewall/helpers/interfaces_test.rb create mode 100644 library/network/test/y2firewall/relations_test.rb delete mode 100644 library/network/testsuite/tests/NO/SuSEFirewall.err delete mode 100644 library/network/testsuite/tests/NO/SuSEFirewall.out delete mode 100644 library/network/testsuite/tests/NO/SuSEFirewall.rb delete mode 100644 library/network/testsuite/tests/NO/SuSEFirewallExpertRules.err delete mode 100644 library/network/testsuite/tests/NO/SuSEFirewallExpertRules.out delete mode 100644 library/network/testsuite/tests/NO/SuSEFirewallExpertRules.rb diff --git a/library/network/src/Makefile.am b/library/network/src/Makefile.am index b16cbd17a..e85415163 100644 --- a/library/network/src/Makefile.am +++ b/library/network/src/Makefile.am @@ -5,7 +5,6 @@ module_DATA = \ modules/SuSEFirewall.rb \ modules/NetworkConfig.rb \ modules/Internet.rb \ - modules/SuSEFirewallProposal.rb \ modules/PortRanges.rb \ modules/NetworkInterfaces.rb \ modules/CWMFirewallInterfaces.rb \ @@ -45,7 +44,8 @@ yfwdlib_DATA = \ lib/y2firewall/firewalld/relations.rb \ lib/y2firewall/firewalld/service.rb \ lib/y2firewall/firewalld/zone.rb \ - lib/y2firewall/firewalld/zone_parser.rb + lib/y2firewall/firewalld/zone_reader.rb \ + lib/y2firewall/firewalld/service_reader.rb yfwdapilibdir = @ylibdir@/y2firewall/firewalld/api/ yfwdapilib_DATA = \ diff --git a/library/network/src/lib/y2firewall/firewalld.rb b/library/network/src/lib/y2firewall/firewalld.rb index 915d53ca8..79c94b673 100644 --- a/library/network/src/lib/y2firewall/firewalld.rb +++ b/library/network/src/lib/y2firewall/firewalld.rb @@ -23,9 +23,11 @@ # *************************************************************************** require "y2firewall/firewalld/api" +require "y2firewall/firewalld/relations" require "y2firewall/firewalld/service" require "y2firewall/firewalld/zone" -require "y2firewall/firewalld/zone_parser" +require "y2firewall/firewalld/zone_reader" +require "y2firewall/firewalld/service_reader" require "singleton" Yast.import "PackageSystem" @@ -49,29 +51,30 @@ class Firewalld include Singleton include Yast::Logger extend Forwardable + extend Relations - # @return Y2Firewall::Firewalld::Api instance - attr_accessor :api - # @return [Array ] firewalld zones + attr_writer :api + # @return [Array] firewalld zones attr_accessor :zones - # @return [Array ] firewalld services. To - # avoid performance problems it is empty by default and the services are - # added when needed by the find_service method. + # @return [Array] current zone names. + attr_accessor :current_zone_names + # @return [Array] current service names. + attr_accessor :current_service_names + # @return [Array] firewalld services. To + # avoid performance problems it is empty by default and the services are + # added when needed by the find_service method. attr_accessor :services - # @return [String] Type of log denied packets (reject & drop rules). - # Possible values are: all, unicast, broadcast, multicast and off - attr_accessor :log_denied_packets - # @return [String] firewalld default zone name - attr_accessor :default_zone PACKAGE = "firewalld".freeze SERVICE = "firewalld".freeze - def_delegators :@api, :enable!, :disable!, :reload, :running? + def_delegators :api, :enable!, :disable!, :reload, :running? + has_attributes :log_denied_packets, :default_zone, cache: true # Constructor def initialize - @api = Api.new + @current_zone_names = [] + @current_service_names = [] @zones = [] @services = [] @read = false @@ -83,9 +86,10 @@ def initialize # @return [Boolean] true def read return false unless installed? - @zones = ZoneParser.new(api.zones, api.list_all_zones).parse - @log_denied_packets = api.log_denied_packets - @default_zone = api.default_zone + @zones = zone_reader.read + @current_zone_names = api.zones + @current_service_names = api.services + read_attributes # The list of services is not read or initialized because takes time and # affects to the performance and also the services are rarely touched. @read = true @@ -95,7 +99,7 @@ def read # # @param name [String] the zone name # @return [Y2Firewall::Firewalld::Zone, nil] the firewalld zone with the - # given name + # given name def find_zone(name) zones.find { |z| z.name == name } end @@ -104,7 +108,7 @@ def find_zone(name) # # @param name [String] the service name # @return [Y2Firewall::Firewalld::Service] the firewalld service with - # the given name + # the given name def find_service(name) services.find { |s| s.name == name } || read_service(name) end @@ -116,11 +120,8 @@ def find_service(name) # @return [Y2Firewall::Firewalld::Service] the recently added service def read_service(name) raise(Service::NotFound, name) unless installed? - service = Y2Firewall::Firewalld::Service.new(name: name) - raise(Service::NotFound, name) if !service.supported? - - service.read - @services << service + service = ServiceReader.new.read(name) + services << service service end @@ -128,10 +129,10 @@ def read_service(name) # since read # # @return [Boolean] true if the config was modified; false otherwise - def modified? - default_zone != api.default_zone || - log_denied_packets != api.log_denied_packets || - zones.any?(&:modified?) + def modified?(*item) + return modified.include?(item.first) if !item.empty? + + !modified.empty? || zones.any?(&:modified?) end # Apply the changes to the modified zones and sets the logging option @@ -143,12 +144,18 @@ def write def write_only return false unless installed? read unless read? - zones.each { |z| z.apply_changes! if z.modified? } - api.log_denied_packets = log_denied_packets if log_denied_packets != api.log_denied_packets - api.default_zone = default_zone if default_zone != api.default_zone + apply_zones_changes! + apply_attributes_changes! + untouched! true end + # Apply the changes done in each of the modified zones. It will create or + # delete all the new or removed zones depending on each case. + def apply_zones_changes! + zones.select(&:modified?).each(&:apply_changes!) + end + # Return a map with current firewalld settings. # # @return [Hash] dump firewalld settings @@ -211,9 +218,23 @@ def start # Return whether the configuration has been read # # @return [Boolean] true if the configuration has been read; false - # otherwise + # otherwise def read? @read end + + # Convenience method to instantiate the firewalld API + def api + @api ||= Api.new + end + + private + + # Convenience method to instantiate a new zone reader + # + # @return [ZoneReader] + def zone_reader + ZoneReader.new(api.zones, api.list_all_zones(verbose: true)) + end end end diff --git a/library/network/src/lib/y2firewall/firewalld/api.rb b/library/network/src/lib/y2firewall/firewalld/api.rb index 91ee63a56..ffa89fa8c 100644 --- a/library/network/src/lib/y2firewall/firewalld/api.rb +++ b/library/network/src/lib/y2firewall/firewalld/api.rb @@ -52,20 +52,22 @@ class Api COMMAND = { offline: "firewall-offline-cmd", running: "firewall-cmd" }.freeze # FIXME: Do not like to define twice PACKAGE = "firewalld".freeze + # Modification commands were applied successfully + SUCCESS = "success".freeze # Determines the mode in which firewalld is running and as consequence the # command to be used. attr_accessor :mode # Constructor + # + # @param mode [Symbol, nil] defines which cmdline should be used if the + # running or the offline one. Possible values are: :offline, :running + # @param permanent [Boolean] whether the configuration should be written + # permanently or in runtime when firewalld is running. def initialize(mode: nil, permanent: true) - @mode = - if mode == :running || running? - :running - else - :offline - end - @permanent = permanent + @mode = mode || (running? ? :running : :offline) + @permanent = !offline? && permanent end # Whether the mode is :offline or not @@ -95,10 +97,12 @@ def running? state == "running" end + # Enables the firewalld service def enable! offline? ? run_command("--enable") : Yast::Service.Enable("firewalld") end + # Disables the firewalld service def disable! offline? ? run_command("--disable") : Yast::Service.Disable("firewalld") end @@ -120,7 +124,7 @@ def state end # Return the default zone - # + # @return [String] default zone def default_zone string_command("--get-default-zone") @@ -129,44 +133,41 @@ def default_zone # Set the default zone # # @param zone [String] The firewall zone - # @return [String] default zone - def default_zone=(zone) - run_command("--set-default-zone=#{zone}") + def modify_default_zone(zone) + modify_command("--set-default-zone=#{zone}") end # Do a reload of the firewall if running. In offline mode just return # true as a reload is not needed to apply the changes. # - # @return [Boolean] The firewalld reload result (exit code) + # @return [Boolean] true if the firewall was reloaded successfully def reload return true if offline? - run_command("--reload") + modify_command("--reload") end # Do a complete reload of the firewall if running. In offline mode just # return true as a reload is not needed to apply the changes # - # @return [Boolean] The firewalld complete-reload result (exit code) + # @return [Boolean] true if the firewall was reloaded completely with success def complete_reload return true if offline? - run_command("--complete-reload") + modify_command("--complete-reload") end ### Logging ### # @param kind [String] Denied packets to log. Possible values are: - # all, unicast, broadcast, multicast and off + # all, unicast, broadcast, multicast and off # @return [Boolean] True if desired packet type is being logged when denied def log_denied_packets?(kind) - string_command("--get-log-denied").strip == kind ? true : false + string_command("--get-log-denied").strip == kind end # @param kind [String] Denied packets to log. Possible values are: - # all, unicast, broadcast, multicast and off - # @return [Boolean] True if desired packet type was set to being logged - # when denied - def log_denied_packets=(kind) - run_command("--set-log-denied=#{kind}") + # all, unicast, broadcast, multicast and off + def modify_log_denied_packets(kind) + modify_command("--set-log-denied=#{kind}") end # @return [String] packet type which is being logged when denied @@ -187,13 +188,12 @@ def command # @see #command # @see Yast::Execute # @param args [Array] list of command optional arguments - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # operates over the permanent configuration # @param allowed_exitstatus [Fixnum, .include?, nil] allowed exit codes - # which do not cause an exception. - # command to be executed + # which do not cause an exception. def run_command(*args, permanent: false, allowed_exitstatus: nil) - arguments = permanent ? ["--permanent"] : [] + arguments = !offline? && permanent ? ["--permanent"] : [] arguments.concat(args) log.info("Executing #{command} with #{arguments.inspect}") @@ -206,22 +206,37 @@ def run_command(*args, permanent: false, allowed_exitstatus: nil) # the output as a string and chomping it # # @see #run_command - # @return [String] the chomped output of the run command # @param args [Array] list of command optional arguments - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # operates over the permanent configuration + # @return [String] the chomped output of the run command def string_command(*args, permanent: false) run_command(*args, permanent: permanent).to_s.chomp end + # Convenience method that run the given modification command for the + # current mode returning whether it was applied successfully or not + # + # + # @see #run_command + # @param args [Array] list of command optional arguments + # @param permanent [Boolean] if true and firewalld is running it + # operates over the permanent configuration + # @return [Boolean] true if the executed command returns succesfully + def modify_command(*args, permanent: false) + string_command(*args, permanent: permanent) == SUCCESS + end + # Convenience method which return true whether the run command for the # current mode return the exit status 0. # # @see #run_command - # @return [Boolean] true if the exit status of the executed command is 0 # @param args [Array] list of command optional arguments - def query_command(*args) - _output, exit_status = run_command(*args, allowed_exitstatus: [0, 1]) + # @param permanent [Boolean] if true and firewalld is running it + # operates over the permanent configuration + # @return [Boolean] true if the exit status of the executed command is 0 + def query_command(*args, permanent: false) + _output, exit_status = run_command(*args, allowed_exitstatus: [0, 1], permanent: permanent) exit_status == 0 end diff --git a/library/network/src/lib/y2firewall/firewalld/api/services.rb b/library/network/src/lib/y2firewall/firewalld/api/services.rb index 96e642a81..2ebecbea4 100644 --- a/library/network/src/lib/y2firewall/firewalld/api/services.rb +++ b/library/network/src/lib/y2firewall/firewalld/api/services.rb @@ -25,71 +25,110 @@ module Y2Firewall class Firewalld class Api - # This module contains specific api methods for handling services + # This module contains specific API methods for handling services # definition and configuration. module Services + # Creates a new service definition for the given service name + # # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed - def new_service(service, permanent: permanent?) - query_command("--new-service=#{service}", permanent: permanent) + # @return [Boolean] true if the new service was created + def create_service(service) + modify_command("--new-service=#{service}", permanent: !offline?) end + # Remove the service definition for the given service name + # + # @param service [String] The firewall service + # @return [Boolean] true if the service was deleted + def delete_service(service) + modify_command("--delete-service=#{service}", permanent: !offline?) + end + + # Return the list of availale firewalld services + # # @return [Array] List of firewall services def services string_command("--get-services").split(" ") end - # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # Show all the service declaration (name, description, ports, + # protocols and modules) + # + # @param service [String] The firewall service name + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] list of all information for the given service def info_service(service, permanent: permanent?) - string_command("--info-service", service.to_s, permanent: permanent).split("\n") + string_command("--info-service=#{service}", "--verbose", permanent: permanent).split("\n") end + # Full name or short description of the service + # # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [String] Short description for service def service_short(service, permanent: permanent?) # these may not exist on early firewalld releases string_command("--service=#{service}", "--get-short", permanent: permanent) end + # Modify the full name or short description of the service + # + # @param service [String] The firewall service + # @param short_description [String] the new service name or description + def modify_service_short(service, short_description) + modify_command("--service=#{service}", "--set-short=#{short_description}", + permanent: !offline?) + end + # @param service [String] the firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [String] Description for service def service_description(service, permanent: permanent?) string_command("--service=#{service}", "--get-description", permanent: permanent) end + # Modify the long description of the service + # + # @param service [String] The firewall service + # @param long_description [String] the new service description + def modify_service_description(service, long_description) + modify_command("--service=#{service}", "--set-description=#{long_description}", + permanent: !offline?) + end + + # Returns whether the service definition for the service name given is + # present or not. + # # @param service [String] The firewall service # @return [Boolean] True if service definition exists def service_supported?(service) services.include?(service) end + # Return the list of ports allowed by the given service + # # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] The firewall service ports def service_ports(service, permanent: permanent?) string_command("--service=#{service}", "--get-ports", permanent: permanent).split(" ") end # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] The firewall service protocols def service_protocols(service, permanent: permanent?) string_command("--service=#{service}", "--get-protocols", permanent: permanent).split(" ") end # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] The firewall service modules def service_modules(service, permanent: permanent?) string_command("--service=#{service}", "--get-modules", permanent: permanent).split(" ") @@ -97,20 +136,20 @@ def service_modules(service, permanent: permanent?) # @param service [String] The firewall service # @param port [String] The firewall port - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if port was removed from service def remove_service_port(service, port, permanent: permanent?) - string_command("--service=#{service}", "--remove-port=#{port}", permanent: permanent) + modify_command("--service=#{service}", "--remove-port=#{port}", permanent: permanent) end # @param service [String] The firewall firewall # @param port [String] The firewall port - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if port was removed from service def add_service_port(service, port, permanent: permanent?) - string_command("--service=#{service}", "--add-port=#{port}", permanent: permanent) + modify_command("--service=#{service}", "--add-port=#{port}", permanent: permanent) end end end diff --git a/library/network/src/lib/y2firewall/firewalld/api/zones.rb b/library/network/src/lib/y2firewall/firewalld/api/zones.rb index 645fb0420..64c8c5028 100644 --- a/library/network/src/lib/y2firewall/firewalld/api/zones.rb +++ b/library/network/src/lib/y2firewall/firewalld/api/zones.rb @@ -25,7 +25,7 @@ module Y2Firewall class Firewalld class Api - # This module contains specific api methods for handling zones + # This module contains specific API methods for handling zones # configuration. module Zones # @return [Array] List of firewall zones @@ -34,54 +34,64 @@ def zones end # @param zone [String] The firewall zone - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] list of zone's interfaces def list_interfaces(zone, permanent: permanent?) string_command("--zone=#{zone}", "--list-interfaces", permanent: permanent).split(" ") end # @param zone [String] The firewall zone - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Arrray] list of zone's services def list_services(zone, permanent: permanent?) string_command("--zone=#{zone}", "--list-services", permanent: permanent).split(" ") end # @param zone [String] The firewall zone + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] list of zone's ports def list_ports(zone, permanent: permanent?) string_command("--zone=#{zone}", "--list-ports", permanent: permanent).split(" ") end # @param zone [String] The firewall zone + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] list of zone's protocols def list_protocols(zone, permanent: permanent?) string_command("--zone=#{zone}", "--list-protocols", permanent: permanent).split(" ") end # @param zone [String] The firewall zone - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] list of zone's sources def list_sources(zone, permanent: permanent?) string_command("--zone=#{zone}", "--list-sources", permanent: permanent).split(" ") end # @param zone [String] The firewall zone - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] list of all information for given zone - def list_all(zone, permanent: permanent?) - string_command("--zone=#{zone}", "--list-all", permanent: permanent).split(" ") + def list_all(zone, permanent: permanent?, verbose: false) + args = ["--zone=#{zone}", "--list-all"] + args << "--verbose" if verbose + + string_command(*args, permanent: permanent).split(" ") end - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Array] list of all information for all firewall zones - def list_all_zones(permanent: permanent?) - string_command("--list-all-zones", permanent: permanent).split("\n") + def list_all_zones(permanent: permanent?, verbose: false) + args = ["--list-all-zones"] + args << "--verbose" if verbose + + string_command(*args, permanent: permanent).split("\n") end ### Interfaces ### @@ -89,175 +99,238 @@ def list_all_zones(permanent: permanent?) # Return the name of the zone the interface belongs to or nil. # # @param interface [String] interface name - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [String, nil] the interface zone or nil def interface_zone(interface, permanent: permanent?) string_command("--get-zone-of-interface=#{interface}", permanent: permanent) end # @param zone [String] The firewall zone + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @param interface [String] The network interface - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed # @return [Boolean] True if interface is assigned to zone def interface_enabled?(zone, interface, permanent: permanent?) - query_command("--zone=#{zone} --query-interface=#{interface}", permanent: permanent) + query_command("--zone=#{zone}", "--query-interface=#{interface}", permanent: permanent) end # @param zone [String] The firewall zone # @param interface [String] The network interface - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if interface was added to zone def add_interface(zone, interface, permanent: permanent?) - run_command("--zone=#{zone}", "--add-interface=#{interface}", permanent: permanent) + modify_command("--zone=#{zone}", "--add-interface=#{interface}", + permanent: permanent) end # @param zone [String] The firewall zone # @param interface [String] The network interface - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed - # @return [Boolean] True if interface was removed from zone + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration + # @return [Boolean] True if the interface was removed from the zone def remove_interface(zone, interface, permanent: permanent?) - run_command("--zone=#{zone}", "--remove-interface=#{interface}", permanent: permanent) + modify_command("--zone=#{zone}", "--remove-interface=#{interface}", + permanent: permanent) end # @param zone [String] The firewall zone # @param interface [String] The network interface - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if interface was changed def change_interface(zone, interface, permanent: permanent?) - run_command("--zone=#{zone}", "--change-interface=#{interface}", permanent: permanent) + modify_command("--zone=#{zone}", "--change-interface=#{interface}", + permanent: permanent) end # @param zone [String] The firewall zone # @param source [String] The network source - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if source was added def add_source(zone, source, permanent: permanent?) - run_command("--zone=#{zone}", "--add-source=#{source}", permanent: permanent) + modify_command("--zone=#{zone}", "--add-source=#{source}", permanent: permanent) end # @param zone [String] The firewall zone # @param source [String] The network source - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if source was removed def remove_source(zone, source, permanent: permanent?) - run_command("--zone=#{zone}", "--remove-source=#{source}", permanent: permanent) + modify_command("--zone=#{zone}", "--remove-source=#{source}", + permanent: permanent) end # @param zone [String] The firewall zone # @param source [String] The network source - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if source was changed def change_source(zone, source, permanent: permanent?) - run_command("--zone=#{zone}", "--change-source=#{source}", permanent: permanent) + modify_command("--zone=#{zone}", "--change-source=#{source}", permanent: permanent) end # @param zone [String] The firewall zone # @param service [String] The firewall service # @return [Boolean] True if service is enabled in zone - def service_enabled?(zone, service) - query_command("--zone=#{zone}", "--query-service=#{service}") + def service_enabled?(zone, service, permanent: permanent?) + query_command("--zone=#{zone}", "--query-service=#{service}", permanent: permanent) end # @param zone [String] The firewall zone # @param port [String] The firewall port # @return [Boolean] True if port is enabled in zone - def port_enabled?(zone, port) - query_command("--zone=#{zone}", "--query-port=#{port}") + def port_enabled?(zone, port, permanent: permanent?) + query_command("--zone=#{zone}", "--query-port=#{port}", permanent: permanent) end # @param zone [String] The firewall zone # @param protocol [String] The zone protocol # @return [Boolean] True if protocol is enabled in zone - def protocol_enabled?(zone, protocol) - query_command("--zone=#{zone}", "--query-protocol=#{protocol}") + def protocol_enabled?(zone, protocol, permanent: permanent?) + query_command("--zone=#{zone}", "--query-protocol=#{protocol}", permanent: permanent) + end + + # @param zone [String] The firewall zone + # @param source [String] The network source + # @return [Boolean] True if the source is binded to the zone + def source_enabled?(zone, source, permanent: permanent?) + query_command("--zone=#{zone}", "--query-source=#{source}", permanent: permanent) end # @param zone [String] The firewall zone # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if service was added to zone def add_service(zone, service, permanent: permanent?) - run_command("--zone=#{zone}", "--add-service=#{service}", permanent: permanent) + modify_command("--zone=#{zone}", "--add-service=#{service}", permanent: permanent) end # @param zone [String] The firewall zone # @param port [String] The firewall port - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if port was added to zone def add_port(zone, port, permanent: permanent?) - run_command("--zone=#{zone}", "--add-port=#{port}", permanent: permanent) + modify_command("--zone=#{zone}", "--add-port=#{port}", permanent: permanent) end # @param zone [String] The firewall zone # @param protocol [String] The firewall protocol - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if protocol was added to zone def add_protocol(zone, protocol, permanent: permanent?) - run_command("--zone=#{zone}", "--add-protocol=#{protocol}", permanent: permanent) + modify_command("--zone=#{zone}", "--add-protocol=#{protocol}", permanent: permanent) end # @param zone [String] The firewall zone # @param service [String] The firewall service - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if service was removed from zone def remove_service(zone, service, permanent: permanent?) - if offline? - run_command("--zone=#{zone}", "--remove-service-from-zone=#{service}") - else - run_command("--zone=#{zone}", "--remove-service=#{service}", permanent: permanent) - end + modify_command("--zone=#{zone}", "--remove-service=#{service}", permanent: permanent) end # @param zone [String] The firewall zone # @param port [String] The firewall port - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if port was removed from zone def remove_port(zone, port, permanent: permanent?) - run_command("--zone=#{zone}", "--remove-port=#{port}", permanent: permanent) + modify_command("--zone=#{zone}", "--remove-port=#{port}", permanent: permanent) end # @param zone [String] The firewall zone # @param protocol [String] The firewall protocol - # @param permanent [Boolean] if true it adds the --permanent option the - # command to be executed + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if protocol was removed from zone def remove_protocol(zone, protocol, permanent: permanent?) - run_command("--zone=#{zone}", "--remove-protocol=#{protocol}", permanent: permanent) + modify_command("--zone=#{zone}", "--remove-protocol=#{protocol}", permanent: permanent) end # @param zone [String] The firewall zone + # @param permanent [Boolean] if true and firewalld is running it + # reads the permanent configuration # @return [Boolean] True if masquerade is enabled in zone - def masquerade_enabled?(zone) - query_command("--zone=#{zone}", "--query-masquerade") + def masquerade_enabled?(zone, permanent: permanent?) + query_command("--zone=#{zone}", "--query-masquerade", permanent: permanent) end # @param zone [String] The firewall zone + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if masquerade was enabled in zone - def add_masquerade(zone) - return true if masquerade_enabled?(zone) + def add_masquerade(zone, permanent: permanent?) + return true if masquerade_enabled?(zone, permanent: permanent) - run_command("--zone=#{zone}", "--add-masquerade") + modify_command("--zone=#{zone}", "--add-masquerade", permanent: permanent) end # @param zone [String] The firewall zone + # @param permanent [Boolean] if true and firewalld is running it + # modifies the permanent configuration # @return [Boolean] True if masquerade was removed in zone - def remove_masquerade(zone) - return true if !masquerade_enabled?(zone) + def remove_masquerade(zone, permanent: permanent?) + return true unless masquerade_enabled?(zone, permanent: permanent) + + modify_command("--zone=#{zone}", "--remove-masquerade", permanent: permanent) + end + + # Full name or short description of the zone + # + # @param zone [String] The firewall zone + def short(zone) + string_command("--zone=#{zone}", "--get-short", permanent: !offline?) + end + + # Modify the full name or short description of the zone + # + # @param zone [String] The firewall zone + # @param short_description [String] the new zone name or description + # @return [Boolean] true if the short description was modified + def modify_short(zone, short_description) + modify_command("--zone=#{zone}", "--set-short=#{short_description}", + permanent: !offline?) + end + + # Long description of the zone + # + # @param zone [String] The firewall zone + def description(zone) + string_command("--zone=#{zone}", "--get-description", permanent: !offline?) + end - run_command("--zone=#{zone}", "--remove-masquerade") + # Modify the long description of the zone + # + # @param zone [String] The firewall zone + # @param long_description [String] the new zone description + # @return [Boolean] true if the long description was modified + def modify_description(zone, long_description) + modify_command("--zone=#{zone}", "--set-description=#{long_description}", + permanent: !offline?) + end + + # The target of the zone + # + # @param zone [String] The firewall zone + def target(zone) + string_command("--zone=#{zone}", "--get-target", permanent: !offline?) + end + + # Modify the current target of the zone + # + # @param zone [String] The firewall zone + # @param target [String] the new target + # @return [Boolean] true if the zone target was modified + def modify_target(zone, target) + modify_command("--zone=#{zone}", "--set-target=#{target}", permanent: !offline?) end end end diff --git a/library/network/src/lib/y2firewall/firewalld/relations.rb b/library/network/src/lib/y2firewall/firewalld/relations.rb index 89aab9679..9d7dcd687 100644 --- a/library/network/src/lib/y2firewall/firewalld/relations.rb +++ b/library/network/src/lib/y2firewall/firewalld/relations.rb @@ -4,8 +4,6 @@ class Firewalld # attributes common logic. module Relations def enable_modifications_cache - attr_writer :modified - # Return an array with all the modified attributes/relations define_method "modified" do @modified ||= [] @@ -31,14 +29,93 @@ def enable_modifications_cache end end + # Defines a set of methods to operate over single-value firewalld + # attributes like name, description, default_zone... Bang! methods + # apply the object modifications into the firewalld configuration + # using the firewalld API. + # + # A modifications cache can be enable with the cache param. + # + # @example + # + # class Zone + # extend Relations + # + # has_attributes :short, :description, :target, cache: true + # end + # + # zone = Zone.new + # + # # Return all the declared attributes + # zone.attributes #=> [:short, :description, :target] + # # Read all the attributes initializing the object + # zone.read_attributes + # # Obtain the configured zone name (not the object one) + # zone.current_short + # # Returns whether the zone has been modified since last read or write + # zone.modified? #=> false + # # Modifies the zone target + # zone.target = "DROP" + # zone.modified? #=> true + # # Apply all the attributes changes in firewalld + # zone.apply_attributes_changes! + # zone.modified? #=> false + # zone.target = "DROP" + # zone.modified? #=> true + # Reset all the modifications + # zone.untouched! + # zone.modified? #=> false + # + # @param attributes [Array] relation or attribute names + # @param scope [String, nil] prepend some API calls with the given scope + # @param cache [Boolean] if enabled will define some methods for caching + # the object modifications + def has_attributes(*attributes, scope: nil, cache: false) # rubocop:disable Style/PredicateName + scope_method = scope ? "#{scope}_" : "" + enable_modifications_cache if cache + define_method "attributes" do + attributes + end + + attributes.each do |attribute| + attr_reader attribute + + define_method "#{attribute}=" do |item| + instance_variable_set("@#{attribute}", item) + + modified!(attribute) if cache + end + + define_method "current_#{attribute}" do + params = ["#{scope_method}#{attribute}"] + params << name if respond_to?("name") + api.public_send(*params) + end + end + + define_method "read_attributes" do + attributes.each { |a| instance_variable_set("@#{a}", public_send("current_#{a}")) } + true + end + + define_method "apply_attributes_changes!" do + attributes.each do |attribute| + next if cache && !modified?(attribute) + params = ["modify_#{scope_method}#{attribute}"] + params << name if respond_to?("name") + params << public_send(attribute) + api.public_send(*params) + end + true + end + end + # Defines a set of methods to operate over array based firewalld # attributes like services, interfaces, protocols, ports... Bang! methods - # applies the object modifications into the firewalld zone using the - # Firewalld API. + # apply the object modifications into the firewalld configuration + # using the firewalld cmdline API. # - # A modifications cache can be enable with the cache param. In that - # case it is important to initialize objects with the instance variable - # @modified as an empty array. + # A modifications cache can be enable with the cache param. # # # @example # @@ -46,10 +123,6 @@ def enable_modifications_cache # extend Relations # # has_many :services, cache: true - # - # def initialize - # @modified = [] - # end # end # # zone = Zone.new @@ -83,7 +156,10 @@ def enable_modifications_cache # # Apply all the relations changes # zone.apply_relations_changes! # - # @param args [Array] relation or attribute names + # @param scope [String, nil] prepend some API calls with the given scope + # @param cache [Boolean] if enabled will define some methods for caching + # the object modifications def has_many(*relations, scope: nil, cache: false) # rubocop:disable Style/PredicateName scope = "#{scope}_" if scope enable_modifications_cache if cache diff --git a/library/network/src/lib/y2firewall/firewalld/service.rb b/library/network/src/lib/y2firewall/firewalld/service.rb index 83be44b72..e85068985 100644 --- a/library/network/src/lib/y2firewall/firewalld/service.rb +++ b/library/network/src/lib/y2firewall/firewalld/service.rb @@ -49,14 +49,11 @@ def initialize(name) include Yast::I18n extend Yast::I18n - # @return name [String] service name + # @return [String] service name attr_reader :name - # @return short [String] service short description - attr_reader :short - # @return description [String] service long description - attr_reader :description has_many :ports, :protocols, scope: "service", cache: true + has_attributes :short, :description, scope: "service", cache: true # Convenience method for setting the tcp and udp ports of a given # service. If the service is found, it modify the ports according to the @@ -86,11 +83,13 @@ def self.modify_ports(name:, tcp_ports: [], udp_ports: []) # @param name [String] zone name def initialize(name:) @name = name + @ports = [] + @protocols = [] end # Create the service in firewalld def create! - api.add_service(name) + api.create_service(name) end # Return whether the service is available in firewalld or not @@ -105,10 +104,8 @@ def supported? # @return [Boolean] true if read def read return false unless supported? - @short = api.service_short(name) - @description = api.service_description(name) - @protocols = current_protocols - @ports = current_ports + read_attributes + read_relations untouched! true end @@ -117,9 +114,10 @@ def read # # @return [Boolean] true if applied; false otherwise def apply_changes! - return false if !supported? || !modified? - apply_protocols_changes! - apply_ports_changes! + return true if !modified? + return false if !supported? + apply_attributes_changes! + apply_relations_changes! untouched! true end diff --git a/library/network/src/lib/y2firewall/firewalld/service_reader.rb b/library/network/src/lib/y2firewall/firewalld/service_reader.rb new file mode 100644 index 000000000..09656aa7c --- /dev/null +++ b/library/network/src/lib/y2firewall/firewalld/service_reader.rb @@ -0,0 +1,59 @@ +# encoding: utf-8 + +# ------------------------------------------------------------------------------ +# Copyright (c) 2018 SUSE LLC +# +# +# 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. +# +# To contact SUSE about this file by physical or electronic mail, you may find +# current contact information at www.suse.com. +# ------------------------------------------------------------------------------ + +require "English" +require "yast" +require "y2firewall/firewalld/api" +require "y2firewall/firewalld/service" + +module Y2Firewall + class Firewalld + # Class to help parsing firewall-cmd --info-service=service output + class ServiceReader + include Yast::Logger + + # @return [Array] + def read(name) + info = Y2Firewall::Firewalld.instance.api.info_service(name) + raise(Service::NotFound, name) if $CHILD_STATUS.exitstatus == 101 + service = Service.new(name: name) + + info.each do |line| + next if line.lstrip.empty? + next if line.start_with?(/#{name}/) + + attribute, value = line.split(":\s") + attribute = attribute.lstrip.tr("-", "_") + attribute = "short" if attribute == "summary" + next unless service.respond_to?("#{attribute}=") + if service.attributes.include?(attribute.to_sym) + service.public_send("#{attribute}=", value.to_s) + else + service.public_send("#{attribute}=", value.to_s.split) + end + end + + service.untouched! + service + end + end + end +end diff --git a/library/network/src/lib/y2firewall/firewalld/zone.rb b/library/network/src/lib/y2firewall/firewalld/zone.rb index d0098d739..307892765 100644 --- a/library/network/src/lib/y2firewall/firewalld/zone.rb +++ b/library/network/src/lib/y2firewall/firewalld/zone.rb @@ -46,12 +46,12 @@ class Zone "work" => N_("Work Zone") }.freeze - # @return [String] Zone name - attr_reader :name - # @see Y2Firewall::Firewalld::Relations has_many :services, :interfaces, :protocols, :ports, :sources, cache: true + # @see Y2Firewall::Firewalld::Relations + has_attributes :name, :short, :description, :target, cache: true + # @return [Boolean] Whether masquerade is enabled or not attr_reader :masquerade @@ -73,7 +73,7 @@ def self.known_zones # Setter method for enabling masquerading. # - # @param enabled [Boolean] true for enable; false for disable + # @param enable [Boolean] true for enable; false for disable # @return [Boolean] whether it is enabled or not def masquerade=(enable) modified!(:masquerade) @@ -94,6 +94,7 @@ def apply_changes! return true unless modified? apply_relations_changes! + apply_attributes_changes! if modified?(:masquerade) masquerade? ? api.add_masquerade(name) : api.remove_masquerade(name) end @@ -130,15 +131,10 @@ def service_open?(service) # # @return [Hash] zone configuration def export - { - "name" => name, - "interfaces" => interfaces, - "services" => services, - "ports" => ports, - "protocols" => protocols, - "sources" => sources, - "masquerade" => masquerade - } + (attributes + relations) + .each_with_object("masquerade" => masquerade) do |field, profile| + profile[field.to_s] = public_send(field) + end end # Override relation method to be more defensive. An interface can only diff --git a/library/network/src/lib/y2firewall/firewalld/zone_parser.rb b/library/network/src/lib/y2firewall/firewalld/zone_parser.rb deleted file mode 100644 index 78a1bc3a9..000000000 --- a/library/network/src/lib/y2firewall/firewalld/zone_parser.rb +++ /dev/null @@ -1,79 +0,0 @@ -# encoding: utf-8 - -# ------------------------------------------------------------------------------ -# Copyright (c) 2017 SUSE LLC -# -# -# 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. -# -# To contact SUSE about this file by physical or electronic mail, you may find -# current contact information at www.suse.com. -# ------------------------------------------------------------------------------ - -require "yast" -require "y2firewall/firewalld/api" - -module Y2Firewall - class Firewalld - # Class to help parsing firewall-cmd --list_all_zones output - class ZoneParser - include Yast::Logger - - BOOLEAN_ATTRIBUTES = ["icmp-block-inversion", "masquerade"].freeze - - # Constructor - # - # @param zone_names [Array] zone names - # @param zones_definition [String] text with the complete definition of - # existing zones. - def initialize(zone_names, zones_definition) - @zone_names = zone_names - @zones_definition = zones_definition - end - - # It parses the zone definition instantiating the defined zones and - # settings their attributes. - # - # @return [Array] - def parse - return [] if !@zone_names || @zone_names.empty? - zone = nil - zones = [] - @zones_definition.reject(&:empty?).each do |line| - attribute, _value = line.split("\s") - next if !attribute - - if @zone_names.include?(attribute) - zone = Zone.new(name: attribute) - zones << zone - next - end - - next unless zone - - attribute, value = line.lstrip.split(":\s") - - next unless zone.respond_to?("#{attribute}=") - if BOOLEAN_ATTRIBUTES.include?(attribute) - zone.public_send("#{attribute}=", value == "yes" ? true : false) - else - zone.public_send("#{attribute}=", value.to_s.split) - end - end - - zones.map(&:untouched!) - - zones - end - end - end -end diff --git a/library/network/src/lib/y2firewall/firewalld/zone_reader.rb b/library/network/src/lib/y2firewall/firewalld/zone_reader.rb new file mode 100644 index 000000000..34c6ae164 --- /dev/null +++ b/library/network/src/lib/y2firewall/firewalld/zone_reader.rb @@ -0,0 +1,132 @@ +# encoding: utf-8 + +# ------------------------------------------------------------------------------ +# Copyright (c) 2018 SUSE LLC +# +# +# 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. +# +# To contact SUSE about this file by physical or electronic mail, you may find +# current contact information at www.suse.com. +# ------------------------------------------------------------------------------ + +require "yast" +require "y2firewall/firewalld/api" +require "y2firewall/firewalld/zone" + +module Y2Firewall + class Firewalld + # Class to help parsing firewall-cmd --list-all-zones output + class ZoneReader + include Yast::Logger + + # @return [Array] stores the parsed configuration for eac + # zone indexed by its name + attr_accessor :zone_entries + # @return [String] zones definition to be parsed for initializing the + # zone objects + attr_accessor :zones_definition + + BOOLEAN_ATTRIBUTES = ["icmp-block-inversion", "masquerade"].freeze + MULTIPLE_ENTRIES = ["rich_rules", "forward_ports"].freeze + + # Constructor + # + # @param zone_names [Array] zone names + # @param zones_definition [String] text with the complete definition of + # existing zones. + def initialize(zone_names, zones_definition) + @zone_names = zone_names + @zones_definition = zones_definition + @zone_entries = {} + end + + # It reads the zone definition instantiating the defined zones and + # settings their attributes. + # + # @return [Array] + def read + return [] if !@zone_names || @zone_names.empty? + parse_zones + initialize_zones + end + + private + + # Iterates over the zones definition filling the zone entries with the + # parsed information of each zone + def parse_zones + current_zone = nil + current_attribute = nil + zones_definition.each_with_object(zone_entries) do |line, entries| + next if line.lstrip.empty? + # If the entry looks like a zone name + if line.start_with?(/\w/) + current_zone = current_zone_from(line) + next + end + + next unless current_zone + + attribute, value = line.split(":\s") + if attribute && attribute.start_with?(/\s\s\w/) + current_attribute = attribute.lstrip.tr("-", "_") + entries[current_zone] ||= {} + entries[current_zone][current_attribute] ||= [value.to_s] + elsif current_attribute + entries[current_zone][current_attribute] ||= [] + entries[current_zone][current_attribute] << line.lstrip + end + end + end + + def current_zone_from(line) + attribute, _value = line.split(/\s*\(active\)\s*$/) + zone_names.include?(attribute) ? attribute : nil + end + + ATTRIBUTE_MAPPING = { "summary" => "short", "rich rules" => "rich_rules" }.freeze + # Iterates over the zone entries instantiating a zone object per each of + # the entries and returning an array with all of them. + # + # @return [Array - - # proposal was changed by user - @proposal_changed_by_user = false - - # proposal was initialized yet - @proposal_initialized = false - - # known interfaces - @known_interfaces = [] - - # warnings for this "turn" - @warnings_now = [] - - @vnc_fallback_ports = ["5801", "5901"] - - # bnc #427708, yet another name of service - @vnc_service = "service:xorg-x11-server" - - @ssh_service = "service:sshd" - end - - # - - # - - # Local function adds another warning string into warnings for user - # - # @param [String] warning - def AddWarning(warning) - @warnings_now = Builtins.add(@warnings_now, warning) - - nil - end - - # Local function clears all warnings for user from memory - def ClearWarnings - @warnings_now = [] - - nil - end - - # Function returns list of warnings for user - # - # @return [Array] of warnings - def GetWarnings - deep_copy(@warnings_now) - end - - # Local function sets currently known interfaces. - # - # @param [Array] interfaces list of known interfaces - def SetKnownInterfaces(interfaces) - interfaces = deep_copy(interfaces) - @known_interfaces = deep_copy(interfaces) - - nil - end - - # Local function returns list [string] of known interfaces. - # They must have been set using SetKnownInterfaces(list [string] interfaces) - # function. - # - # @return [Array] of known interfaces - def GetKnownInterfaces - deep_copy(@known_interfaces) - end - - # Function returns if interface is a dial-up type. - # - # @return [Boolean] if is dial-up interface - def IsDialUpInterface(interface) - all_interfaces = SuSEFirewall.GetAllKnownInterfaces - - interface_type = nil - Builtins.foreach(all_interfaces) do |one| - next if Ops.get(one, "id") != interface - # this is THE interface - interface_type = Ops.get(one, "type") - end - - interface_type == "dialup" - end - - # Local function adds list of interfaces into zone. - # - # @param [Array] interfaces - # @param [String] zone - def SetInterfacesToZone(interfaces, zone) - interfaces = deep_copy(interfaces) - Builtins.foreach(interfaces) do |interface| - SuSEFirewall.AddInterfaceIntoZone(interface, zone) - end - - nil - end - - # Local function for updating user-changed proposal. - def UpdateProposal - last_known_interfaces = GetKnownInterfaces() - currently_known_interfaces = SuSEFirewall.GetListOfKnownInterfaces - - had_dialup_interfaces = false - Builtins.foreach(last_known_interfaces) do |this_interface| - if IsDialUpInterface(this_interface) - had_dialup_interfaces = true - raise Break - end - end - - Builtins.foreach(currently_known_interfaces) do |interface| - # already known but not assigned - next if Builtins.contains(last_known_interfaces, interface) - # already configured in some zone - next if !SuSEFirewall.GetZoneOfInterface(interface).nil? - # any dial-up interfaces presented and the new one isn't dial-up - if had_dialup_interfaces && !IsDialUpInterface(interface) - AddWarning( - Builtins.sformat( - # TRANSLATORS: Warning in installation proposal, %1 is a device name (eth0, sl0, ...) - _( - "New network device '%1' found; added as an internal firewall interface" - ), - interface - ) - ) - SetInterfacesToZone([interface], "INT") - else - AddWarning( - Builtins.sformat( - # TRANSLATORS: Warning in installation proposal, %1 is a device name (eth0, sl0, ...) - _( - "New network device '%1' found; added as an external firewall interface" - ), - interface - ) - ) - SetInterfacesToZone([interface], "EXT") - end - end - - SetKnownInterfaces(currently_known_interfaces) - - nil - end - - # Returns whether service is enabled in zones. - # - # @param [String] service - # @param [Array] zones - # @return [Boolean] if enabled - def ServiceEnabled(service, zones) - zones = deep_copy(zones) - if service.nil? || service == "" - Builtins.y2error("Ups, service: %1?", service) - return false - end - - if zones.nil? || zones == [] - Builtins.y2error("Ups, zones: %1?", zones) - return false - end - - serenabled = true - - serstat = SuSEFirewall.GetServices([service]) - Builtins.foreach(zones) do |one_zone| - if Ops.get(serstat, [service, one_zone]) == false - Builtins.y2milestone( - "Service %1 is not enabled in %2", - service, - one_zone - ) - serenabled = false - raise Break - end - end - - serenabled - end - - # Enables ports in zones. - # - # @param [Array] fallback_ports fallback TCP ports - # @param [Array] zones - def EnableFallbackPorts(fallback_ports, zones) - known_zones = SuSEFirewall.GetKnownFirewallZones() - unknown_zones = zones - known_zones - raise "Unknown firewall zones #{unknown_zones}" unless unknown_zones.empty? - - log.info "Enabling fallback ports: #{fallback_ports} in zones: #{zones}" - zones.each do |one_zone| - fallback_ports.each do |one_port| - SuSEFirewall.AddService(one_port, "TCP", one_zone) - end - end - - nil - end - - # Function opens service for network interfaces given as the third parameter. - # Fallback ports are used if the given service is uknown. - # If interfaces are not assigned to any firewall zone, all zones will be used. - # - # @see OpenServiceOnNonDialUpInterfaces for more info. - # - # @param [String] service e.g., "service:http-server" - # @param [Array] fallback_ports e.g., ["80"] - # @param [Array] interfaces e.g., ["eth3"] - def OpenServiceInInterfaces(service, fallback_ports, interfaces) - fallback_ports = deep_copy(fallback_ports) - interfaces = deep_copy(interfaces) - zones = SuSEFirewall.GetZonesOfInterfaces(interfaces) - - # Interfaces might not be assigned to any zone yet, use all zones - zones = SuSEFirewall.GetKnownFirewallZones() if zones.empty? - - if SuSEFirewallServices.IsKnownService(service) - log.info "Opening service #{service} on interfaces #{interfaces} (zones #{zones})" - SuSEFirewall.SetServicesForZones([service], zones, true) - else - log.warn "Unknown service #{service}, enabling fallback ports" - EnableFallbackPorts(fallback_ports, zones) - end - - nil - end - - # Checks whether the given service or (TCP) ports are open at least in - # one FW zone. - # - # @param [String] service e.g., "service:http-server" - # @param [Array] fallback_ports e.g., ["80"] - def IsServiceOrPortsOpen(service, fallback_ports) - fallback_ports = deep_copy(fallback_ports) - ret = false - - Builtins.foreach(SuSEFirewall.GetKnownFirewallZones) do |zone| - # either service is supported - if SuSEFirewall.IsServiceSupportedInZone(service, zone) - ret = true - # or check for ports - else - all_ports = true - - # all ports have to be open - Builtins.foreach(fallback_ports) do |port| - if !SuSEFirewall.HaveService(port, "TCP", zone) - all_ports = false - raise Break - end - end - - ret = true if all_ports - end - raise Break if ret == true - end - - ret - end - - # Function opens up the service on all non-dial-up network interfaces. - # If there are no network interfaces known and the 'any' feature is supported, - # function opens the service for the zone supporting that feature. If there - # are only dial-up interfaces, function opens the service for them. - # - # @param [String] service such as "service:koo" or "serice:boo" - # @param [Array] fallback_ports list of ports used as a fallback if the given service doesn't exist - def OpenServiceOnNonDialUpInterfaces(service, fallback_ports) - fallback_ports = deep_copy(fallback_ports) - non_dial_up_interfaces = SuSEFirewall.GetAllNonDialUpInterfaces - dial_up_interfaces = SuSEFirewall.GetAllDialUpInterfaces - - # Opening the service for non-dial-up interfaces - if Ops.greater_than(Builtins.size(non_dial_up_interfaces), 0) - OpenServiceInInterfaces(service, fallback_ports, non_dial_up_interfaces) - # Only dial-up network interfaces, there mustn't be any non-dial-up one - elsif Ops.greater_than(Builtins.size(dial_up_interfaces), 0) - OpenServiceInInterfaces(service, fallback_ports, dial_up_interfaces) - # No network interfaces are known - elsif Builtins.size(@known_interfaces) == 0 - if SuSEFirewall.IsAnyNetworkInterfaceSupported == true - Builtins.y2warning( - "WARNING: Opening %1 for the External zone without any known interface!", - Builtins.toupper(service) - ) - OpenServiceInInterfaces( - service, - fallback_ports, - [SuSEFirewall.special_all_interface_string] - ) - end - end - - nil - end - - # Local function returns whether the Xen kernel is installed - # - # @return [Boolean] whether xen-capable kernel is installed. - def IsXenInstalled - # bug #154133 - return true if Package.Installed("kernel-xen") - return true if Package.Installed("kernel-xenpae") - - false - end - - # Local function for proposing firewall configuration. - def ProposeFunctions - known_interfaces = SuSEFirewall.GetAllKnownInterfaces - - dial_up_interfaces = [] - non_dup_interfaces = [] - Builtins.foreach(known_interfaces) do |interface| - if Ops.get(interface, "type") == "dial_up" - dial_up_interfaces = Builtins.add( - dial_up_interfaces, - Ops.get(interface, "id", "") - ) - else - non_dup_interfaces = Builtins.add( - non_dup_interfaces, - Ops.get(interface, "id", "") - ) - end - end - - Builtins.y2milestone( - "Proposal based on configuration: Dial-up interfaces: %1, Other: %2", - dial_up_interfaces, - non_dup_interfaces - ) - - # has any network interface - if Builtins.size(non_dup_interfaces) == 0 || - Builtins.size(dial_up_interfaces) == 0 - SuSEFirewall.SetEnableService( - ProductFeatures.GetBooleanFeature("globals", "enable_firewall") - ) - SuSEFirewall.SetStartService( - ProductFeatures.GetBooleanFeature("globals", "enable_firewall") - ) - end - - # has non-dial-up and also dial-up interfaces - if Ops.greater_than(Builtins.size(non_dup_interfaces), 0) && - Ops.greater_than(Builtins.size(dial_up_interfaces), 0) - SetInterfacesToZone(non_dup_interfaces, "INT") - SetInterfacesToZone(dial_up_interfaces, "EXT") - if ProductFeatures.GetBooleanFeature("globals", "firewall_enable_ssh") - SuSEFirewall.SetServicesForZones([@ssh_service], ["INT", "EXT"], true) - end - - # has non-dial-up and doesn't have dial-up interfaces - elsif Ops.greater_than(Builtins.size(non_dup_interfaces), 0) && - Builtins.size(dial_up_interfaces) == 0 - SetInterfacesToZone(non_dup_interfaces, "EXT") - if ProductFeatures.GetBooleanFeature("globals", "firewall_enable_ssh") - SuSEFirewall.SetServicesForZones([@ssh_service], ["EXT"], true) - end - - # doesn't have non-dial-up and has dial-up interfaces - elsif Builtins.size(non_dup_interfaces) == 0 && - Ops.greater_than(Builtins.size(dial_up_interfaces), 0) - SetInterfacesToZone(dial_up_interfaces, "EXT") - if ProductFeatures.GetBooleanFeature("globals", "firewall_enable_ssh") - SuSEFirewall.SetServicesForZones([@ssh_service], ["EXT"], true) - end - end - - # Dial-up interfaces are considered to be internal, - # Non-dial-up are considered to be external. - # If there are only Non-dial-up interfaces, they are all considered as external. - # - # VNC Installation proposes to open VNC Access up on the Non-dial-up interfaces only. - # SSH Installation is the same case... - if Linuxrc.vnc - Builtins.y2milestone( - "This is an installation over VNC, opening VNC on all non-dial-up interfaces..." - ) - # Try the service first, then ports - # bnc #398855 - OpenServiceOnNonDialUpInterfaces(@vnc_service, @vnc_fallback_ports) - end - if Linuxrc.usessh - Builtins.y2milestone( - "This is an installation over SSH, opening SSH on all non-dial-up interfaces..." - ) - # Try the service first, then ports - # bnc #398855 - OpenServiceOnNonDialUpInterfaces(@ssh_service, ["ssh"]) - end - - # Firewall support for XEN domain0 - if IsXenInstalled() - Builtins.y2milestone( - "Adding Xen support into the firewall configuration" - ) - SuSEFirewall.AddXenSupport - end - - SetKnownInterfaces(SuSEFirewall.GetListOfKnownInterfaces) - - nil - end - - # - - # - - # Function sets that proposal was changed by user - # - # @param changed [true, false] if changed by user - def SetChangedByUser(changed) - Builtins.y2milestone("Proposal was changed by user") - @proposal_changed_by_user = changed - - nil - end - - # Local function returns if proposal was changed by user - # - # @return [Boolean] if proposal was changed by user - def GetChangedByUser - @proposal_changed_by_user - end - - # Function sets that proposal was initialized - # - # @param initialized [true, false] if initialized - def SetProposalInitialized(initialized) - @proposal_initialized = initialized - - nil - end - - # Local function returns if proposal was initialized already - # - # @return [Boolean] if proposal was initialized - def GetProposalInitialized - @proposal_initialized - end - - # Function fills up default configuration into internal values - # - # @return [void] - def Reset - SuSEFirewall.ResetReadFlag - SuSEFirewall.Read - - nil - end - - # Function proposes the SuSEfirewall2 configuration - # - # @return [void] - def Propose - # No proposal when SuSEfirewall2 is not installed - if !SuSEFirewall.SuSEFirewallIsInstalled - SuSEFirewall.SetEnableService(false) - SuSEFirewall.SetStartService(false) - return nil - end - - # Not changed by user - Propose from scratch - if !GetChangedByUser() - Builtins.y2milestone("Calling firewall configuration proposal") - Reset() - ProposeFunctions() - # Changed - don't break user's configuration - else - Builtins.y2milestone("Calling firewall configuration update proposal") - UpdateProposal() - end - - nil - end - - # Function returns the proposal summary - # - # @return [Hash{String => String}] proposal - # - # **Structure:** - # - # map $[ - # "output" : "HTML Proposal Summary", - # "warning" : "HTML Warning Summary", - # ] - def ProposalSummary - # output: $[ "output" : "HTML Proposal", "warning" : "HTML Warning" ]; - output = "" - warning = "" - - # SuSEfirewall2 package needn't be installed - if !SuSEFirewall.SuSEFirewallIsInstalled - # TRANSLATORS: Proposal informative text - output = "
    " + - _( - "SuSEfirewall2 package is not installed, firewall will be disabled." - ) + "
" - - return { "output" => output, "warning" => warning } - end - - # SuSEfirewall2 is installed... - - firewall_is_enabled = SuSEFirewall.GetEnableService == true - - output = Ops.add(output, "
    \n") - output = Ops.add( - Ops.add( - Ops.add(output, "
  • "), - if firewall_is_enabled - # TRANSLATORS: Proposal informative text "Firewall is enabled (disable)" with link around - # IMPORTANT: Please, do not change the HTML link ..., only visible text - _( - "Firewall is enabled (disable)" - ) - else - # TRANSLATORS: Proposal informative text "Firewall is disabled (enable)" with link around - # IMPORTANT: Please, do not change the HTML link ..., only visible text - _( - "Firewall is disabled (enable)" - ) - end - ), - "
  • \n" - ) - - if firewall_is_enabled - # Any enabled SSH means SSH-is-enabled - is_ssh_enabled = false - - # Any known interfaces - if Ops.greater_than(Builtins.size(@known_interfaces), 0) - Builtins.y2milestone("Interfaces: %1", @known_interfaces) - - # all known interfaces for testing - used_zones = SuSEFirewall.GetZonesOfInterfacesWithAnyFeatureSupported( - @known_interfaces - ) - Builtins.y2milestone("Zones used by firewall: %1", used_zones) - - Builtins.foreach(used_zones) do |zone| - if SuSEFirewall.IsServiceSupportedInZone(@ssh_service, zone) || - SuSEFirewall.HaveService("ssh", "TCP", zone) - is_ssh_enabled = true - end - end - - output = Ops.add( - Ops.add( - Ops.add(output, "
  • "), - if is_ssh_enabled - # TRANSLATORS: Network proposal informative text with link around - # IMPORTANT: Please, do not change the HTML link ..., only visible text - _( - "SSH port is open (close)" - ) - else - # TRANSLATORS: Network proposal informative text with link around - # IMPORTANT: Please, do not change the HTML link ..., only visible text - _( - "SSH port is blocked (open)" - ) - end - ), - "
  • \n" - ) - - # No known interfaces, but 'any' is supported - # and ssh is enabled there - elsif SuSEFirewall.IsAnyNetworkInterfaceSupported && - SuSEFirewall.IsServiceSupportedInZone( - @ssh_service, - SuSEFirewall.special_all_interface_zone - ) - is_ssh_enabled = true - # TRANSLATORS: Network proposal informative text with link around - # IMPORTANT: Please, do not change the HTML link ..., only visible text - output = Ops.add( - Ops.add( - Ops.add(output, "
  • "), - _( - "SSH port is open (close), but\nthere are no network interfaces configured" - ) - ), - "
  • " - ) - end - Builtins.y2milestone( - "SSH is " + (is_ssh_enabled ? "" : "not ") + "enabled" - ) - - if Linuxrc.usessh - if !is_ssh_enabled - # TRANSLATORS: This is a warning message. Installation over SSH without SSH allowed on firewall - AddWarning( - _( - "You are installing a system over SSH, but you have not opened the SSH port on the firewall." - ) - ) - end - end - - # when the firewall is enabled and we are installing the system over VNC - if Linuxrc.vnc - # Any enabled VNC means VNC-is-enabled - is_vnc_enabled = false - if Ops.greater_than(Builtins.size(@known_interfaces), 0) - Builtins.foreach( - SuSEFirewall.GetZonesOfInterfacesWithAnyFeatureSupported( - @known_interfaces - ) - ) do |zone| - if SuSEFirewall.IsServiceSupportedInZone(@vnc_service, zone) == true - is_vnc_enabled = true - # checking also fallback ports - else - set_vnc_enabled_to = true - Builtins.foreach(@vnc_fallback_ports) do |one_port| - if SuSEFirewall.HaveService(one_port, "TCP", zone) != true - set_vnc_enabled_to = false - raise Break - end - is_vnc_enabled = true if set_vnc_enabled_to == true - end - end - end - end - Builtins.y2milestone( - "VNC port is " + (is_vnc_enabled ? "open" : "blocked") + " in the firewall" - ) - - output = Ops.add( - Ops.add( - Ops.add(output, "
  • "), - if is_vnc_enabled - # TRANSLATORS: Network proposal informative text "Remote Administration (VNC) is enabled" with link around - # IMPORTANT: Please, do not change the HTML link ..., only visible text - _( - "Remote Administration (VNC) ports are open (close)" - ) - else - # TRANSLATORS: Network proposal informative text "Remote Administration (VNC) is disabled" with link around - # IMPORTANT: Please, do not change the HTML link ..., only visible text - _( - "Remote Administration (VNC) ports are blocked (open)" - ) - end - ), - "
  • \n" - ) - - if !is_vnc_enabled - # TRANSLATORS: This is a warning message. Installation over VNC without VNC allowed on firewall - AddWarning( - _( - "You are installing a system using remote administration (VNC), but you have not opened the VNC ports on the firewall." - ) - ) - end - end - - if Linuxrc.useiscsi - is_iscsi_enabled = IsServiceOrPortsOpen( - @iscsi_target_service, - @iscsi_target_fallback_ports - ) - - output = Ops.add( - Ops.add( - Ops.add(output, "
  • "), - if is_iscsi_enabled - # TRANSLATORS: Network proposal informative text - _("iSCSI Target ports are open") - else - # TRANSLATORS: Network proposal informative text - _("iSCSI Target ports are blocked") - end - ), - "
  • \n" - ) - - if !is_iscsi_enabled - # TRANSLATORS: This is a warning message. Installation to iSCSI without iSCSI allowed on firewall - AddWarning( - _( - "You are installing a system using iSCSI Target, but you have not opened the needed ports on the firewall." - ) - ) - end - end - - warnings_strings = GetWarnings() - if Ops.greater_than(Builtins.size(warnings_strings), 0) - ClearWarnings() - Builtins.foreach(warnings_strings) do |single_warning| - warning = Ops.add( - Ops.add(Ops.add(warning, "
  • "), single_warning), - "
  • \n" - ) - end - warning = Ops.add(Ops.add("
      \n", warning), "
    \n") - end - end - - output = Ops.add(output, "
\n") - - { "output" => output, "warning" => warning } - end - - publish function: :OpenServiceOnNonDialUpInterfaces, type: "void (string, list )" - publish function: :SetChangedByUser, type: "void (boolean)" - publish function: :GetChangedByUser, type: "boolean ()" - publish function: :SetProposalInitialized, type: "void (boolean)" - publish function: :GetProposalInitialized, type: "boolean ()" - publish function: :Reset, type: "void ()" - publish function: :Propose, type: "void ()" - publish function: :ProposalSummary, type: "map ()" - end - - SuSEFirewallProposal = SuSEFirewallProposalClass.new - SuSEFirewallProposal.main -end diff --git a/library/network/test/Makefile.am b/library/network/test/Makefile.am index 6f92c513e..d0b2fcd21 100644 --- a/library/network/test/Makefile.am +++ b/library/network/test/Makefile.am @@ -3,7 +3,6 @@ TESTS = \ network_interfaces_test.rb \ network_interfaces_helpers_test.rb \ network_service_test.rb \ - susefirewall_proposal_test.rb \ susefirewall_services_test.rb \ susefirewall_test.rb \ susefirewalld_test.rb diff --git a/library/network/test/cwm_firewall_interfaces_test.rb b/library/network/test/cwm_firewall_interfaces_test.rb index 4eb70b1be..7dbe09c14 100755 --- a/library/network/test/cwm_firewall_interfaces_test.rb +++ b/library/network/test/cwm_firewall_interfaces_test.rb @@ -8,20 +8,21 @@ Yast.import "UI" describe Yast::CWMFirewallInterfaces do - subject { Yast::CWMFirewallInterfaces } + let(:firewalld) { Y2Firewall::Firewalld.instance } + let(:api) { instance_double("Y2Firewall::Firewalld::Api") } + + before do + allow(api).to receive(:service_supported?) + allow(firewalld).to receive(:api).and_return(api) + end describe "#CreateOpenFirewallWidget" do let(:widget_settings) { { "services" => [] } } - let(:api) { Y2Firewall::Firewalld::Api.new } let(:installed) { true } before do - allow_any_instance_of(Y2Firewall::Firewalld) - .to receive(:api).and_return(api) - allow_any_instance_of(Y2Firewall::Firewalld) - .to receive(:installed?).and_return(installed) - allow(api).to receive(:service_supported?) + allow(firewalld).to receive(:installed?).and_return(installed) end context "when firewalld is not installed" do @@ -198,6 +199,18 @@ end describe "#Selected2Opened" do + let(:known_interfaces) do + [ + { "id" => "eth0", "name" => "Ethernet 1", "zone" => "external" }, + { "id" => "eth1", "name" => "Ethernet 2", "zone" => "public" }, + { "id" => "eth2", "name" => "Ethernet 3", "zone" => "dmz" } + ] + end + + before do + allow(subject).to receive(:known_interfaces).and_return(known_interfaces) + end + context "given a list of selected interfaces" do let(:zone) do instance_double("Y2Firewall::Firewalld::Zone", interfaces: ["eth0", "eth1"], name: "public") @@ -205,7 +218,7 @@ before do allow(subject).to receive(:interface_zone).with("eth0").and_return("public") - allow_any_instance_of(Y2Firewall::Firewalld).to receive(:find_zone).and_return(zone) + allow(firewalld).to receive(:find_zone).and_return(zone) end it "returns all the interfaces that belongs to same zone of the given interfaces" do @@ -222,10 +235,12 @@ { "id" => "eth2", "name" => "Ethernet 3", "zone" => nil } ] end + let(:external_zone) do instance_double("Y2Firewall::Firewalld::Zone", name: "external", interfaces: ["eth0"], services: []) end + let(:public_zone) do instance_double("Y2Firewall::Firewalld::Zone", name: "public", interfaces: ["eth1"], services: ["dns"]) @@ -234,8 +249,9 @@ let(:zones) { [external_zone, public_zone] } before do - allow(subject).to receive(:known_interfaces).and_return(known_interfaces) - allow_any_instance_of(Y2Firewall::Firewalld).to receive(:zones).and_return(zones) + expect(subject).to receive(:known_interfaces).and_return(known_interfaces) + expect(subject).to receive(:allowed_interfaces).and_return(["eth0", "eth1"]) + allow(firewalld).to receive(:zones).and_return(zones) allow(subject).to receive(:default_zone).and_return(public_zone) allow(subject).to receive(:configuration_changed).and_return(true) allow(subject).to receive(:allowed_interfaces).and_return(["eth0", "eth1", "eth2"]) diff --git a/library/network/test/susefirewall_proposal_test.rb b/library/network/test/susefirewall_proposal_test.rb deleted file mode 100755 index ee7dbe9dd..000000000 --- a/library/network/test/susefirewall_proposal_test.rb +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "test_helper" - -Yast.import "SuSEFirewall" -Yast.import "SuSEFirewallServices" -Yast.import "SuSEFirewallProposal" -Yast.import "Linuxrc" - -describe Yast::SuSEFirewallProposal do - subject { Yast::SuSEFirewallProposal } - - describe "#EnableFallbackPorts" do - let(:fallback_ports) { ["port1", "port2"] } - - before(:each) do - allow(Yast::SuSEFirewall).to receive(:GetKnownFirewallZones).and_return(["EXT", "INT", "DMZ"]) - end - - context "when opening ports in known firewall zones" do - it "opens given ports in firewall in given zones" do - expect(Yast::SuSEFirewall).to receive(:AddService).with(/port.*/, "TCP", /(EXT|DMZ)/).exactly(4).times - - subject.EnableFallbackPorts(fallback_ports, ["EXT", "DMZ"]) - end - end - - context "when opening ports in unknown firewall zones" do - it "throws an exception" do - method_call = proc { subject.EnableFallbackPorts(fallback_ports, ["UNKNOWN_ZONE1", "UZ2"]) } - expect { method_call.call }.to raise_error(/UNKNOWN_ZONE1.*UZ2/) - end - end - end - - describe "#OpenServiceInInterfaces" do - let(:network_interfaces) { ["eth-x", "eth-y"] } - let(:interfaces_zones) { ["ZONE1", "ZONE2"] } - let(:all_zones) { ["ZONE1", "ZONE2", "ZONE3"] } - let(:firewall_service) { "service:fw_service_x" } - let(:fallback_ports) { ["p1", "p2", "p3"] } - - before(:each) do - # Default behavior: Interfaces are assigned to zones, there are more known zones, - # given firewall service exists - allow(Yast::SuSEFirewall).to receive(:GetZonesOfInterfaces).and_return(interfaces_zones) - allow(Yast::SuSEFirewall).to receive(:GetKnownFirewallZones).and_return(all_zones) - allow(Yast::SuSEFirewallServices).to receive(:IsKnownService).and_return(true) - end - - context "when network interfaces are assigned to some zone(s)" do - it "open service in firewall in zones that include given interfaces" do - expect(Yast::SuSEFirewall).to receive(:SetServicesForZones).with([firewall_service], interfaces_zones, true) - subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) - end - end - - context "when network interfaces are not assigned to any zone" do - it "opens service in firewall in all zones" do - allow(Yast::SuSEFirewall).to receive(:GetZonesOfInterfaces).and_return([]) - expect(Yast::SuSEFirewall).to receive(:SetServicesForZones).with([firewall_service], all_zones, true) - subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) - end - end - - context "when given firewall service is known" do - it "opens service in firewall in zones that include given interfaces" do - expect(Yast::SuSEFirewall).to receive(:SetServicesForZones).with([firewall_service], interfaces_zones, true) - subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) - end - end - - context "when given service is unknown" do - it "opens given fallback ports in zones that include given interfaces" do - allow(Yast::SuSEFirewallServices).to receive(:IsKnownService).and_return(false) - expect(subject).to receive(:EnableFallbackPorts).with(fallback_ports, interfaces_zones) - subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) - end - end - end -end diff --git a/library/network/test/y2firewall/firewalld/api/services_test.rb b/library/network/test/y2firewall/firewalld/api/services_test.rb new file mode 100755 index 000000000..204e6586b --- /dev/null +++ b/library/network/test/y2firewall/firewalld/api/services_test.rb @@ -0,0 +1,172 @@ +#!/usr/bin/env rspec +# encoding: utf-8 +# +# Copyright (c) 2018 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_relative "../../../test_helper" +require "y2firewall/firewalld/api" + +describe Y2Firewall::Firewalld::Api::Services do + subject(:api) { Y2Firewall::Firewalld::Api.new(mode: :offline) } + + describe "#create_service" do + it "creates a new service definition with the given name" do + expect(api).to receive(:modify_command).with("--new-service=test", permanent: false) + + subject.create_service("test") + end + + it "returns whether the service was created successfully or not" do + allow(api).to receive(:modify_command) + .with("--new-service=test", permanent: false).and_return(true, false) + expect(subject.create_service("test")).to eql(true) + expect(subject.create_service("test")).to eql(false) + end + end + + describe "#remove_service" do + it "deletes the given name service definition" do + expect(api).to receive(:modify_command).with("--delete-service=test", permanent: false) + + subject.delete_service("test") + end + + it "returns whether the service was deleted successfully or not" do + allow(api).to receive(:modify_command) + .with("--delete-service=test", permanent: false).and_return(true, false) + expect(subject.delete_service("test")).to eql(true) + expect(subject.delete_service("test")).to eql(false) + end + end + + describe "#services" do + let(:defined_services) { "amanda-client amanda-k5-client amqp amqps apache2" } + it "obtains the list of firewalld defined services" do + allow(api).to receive(:string_command).with("--get-services").and_return(defined_services) + + expect(subject.services).to eql(defined_services.split(" ")) + end + end + + describe "#service_short" do + it "obtains the service short description" do + allow(api).to receive(:string_command) + .with("--service=test", "--get-short", permanent: api.permanent?) + .and_return("Test short") + + expect(subject.service_short("test")).to eql("Test short") + end + end + + describe "#modify_service_short" do + it "modifies the service short description" do + expect(api).to receive(:modify_command) + .with("--service=test", "--set-short=Modified", permanent: api.permanent?) + + subject.modify_service_short("test", "Modified") + end + end + + describe "#service_description" do + let(:description) { "This is the long description of the test service." } + it "obtains the service long description of the given service" do + allow(api).to receive(:string_command) + .with("--service=test", "--get-description", permanent: api.permanent?) + .and_return(description) + + expect(subject.service_description("test")).to eql(description) + end + end + + describe "#modify_service_description" do + it "modifies the service long description" do + expect(api).to receive(:modify_command) + .with("--service=test", "--set-description=Modified Long", permanent: api.permanent?) + + subject.modify_service_description("test", "Modified Long") + end + end + + describe "#service_supported?" do + before do + allow(api).to receive(:services).and_return(["ssh", "samba"]) + end + + context "when the given service is in the list of the defined ones" do + it "returns true" do + expect(api.service_supported?("ssh")).to eql(true) + end + end + + context "when the given service is not included the list of the defined ones" do + it "returns false" do + expect(api.service_supported?("apache")).to eql(false) + end + end + end + + describe "#service_ports" do + it "returns the list of ports used by the given service" do + allow(api).to receive(:string_command) + .with("--service=test", "--get-ports", permanent: api.permanent?) + .and_return("80/tcp 443/tcp") + + expect(api.service_ports("test")).to eql(["80/tcp", "443/tcp"]) + end + end + + describe "#service_protocols" do + it "returns the list of protocols used by the given service" do + allow(api).to receive(:string_command) + .with("--service=test", "--get-protocols", permanent: api.permanent?) + .and_return("egp gre") + + expect(api.service_protocols("test")).to eql(["egp", "gre"]) + end + end + + describe "#service_modules" do + it "returns the list of netfilter kernel helpers loaded by the given service" do + allow(api).to receive(:string_command) + .with("--service=test", "--get-modules", permanent: api.permanent?) + .and_return("ftp") + + expect(api.service_modules("test")).to eql(["ftp"]) + end + end + + describe "#remove_service_port" do + it "removes the given port from the given service configured ports" do + expect(api).to receive(:modify_command) + .with("--service=test", "--remove-port=80/tcp", permanent: api.permanent?) + + api.remove_service_port("test", "80/tcp") + end + end + + describe "#add_service_port" do + it "adds the given port to the given service configured ports" do + expect(api).to receive(:modify_command) + .with("--service=test", "--add-port=80/tcp", permanent: api.permanent?) + + api.add_service_port("test", "80/tcp") + end + end +end diff --git a/library/network/test/y2firewall/firewalld/api/zones_test.rb b/library/network/test/y2firewall/firewalld/api/zones_test.rb new file mode 100755 index 000000000..83ed8f725 --- /dev/null +++ b/library/network/test/y2firewall/firewalld/api/zones_test.rb @@ -0,0 +1,285 @@ +#!/usr/bin/env rspec +# encoding: utf-8 +# +# Copyright (c) 2018 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_relative "../../../test_helper" +require "y2firewall/firewalld/api" + +describe Y2Firewall::Firewalld::Api::Zones do + subject(:api) { Y2Firewall::Firewalld::Api.new(mode: :offline) } + + describe "#zones" do + let(:known_zones) { "dmz drop external home internal public trusted work" } + it "obtains the list of firewalld defined zones" do + allow(api).to receive(:string_command).with("--get-zones").and_return(known_zones) + + expect(subject.zones).to eql(known_zones.split(" ")) + end + end + + describe "#short" do + it "obtains the zone short description" do + allow(api).to receive(:string_command) + .with("--zone=test", "--get-short", permanent: api.permanent?) + .and_return("Test short") + + expect(subject.short("test")).to eql("Test short") + end + end + + describe "#modify_short" do + it "modifies the zone short description" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--set-short=Modified", permanent: api.permanent?) + + subject.modify_short("test", "Modified") + end + end + + describe "#description" do + let(:description) { "This is the long description of the test zone." } + it "obtains the zone long description" do + allow(api).to receive(:string_command) + .with("--zone=test", "--get-description", permanent: api.permanent?) + .and_return(description) + + expect(subject.description("test")).to eql(description) + end + end + + describe "#modify_description" do + it "modifies the zone long description" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--set-description=Modified Long", permanent: api.permanent?) + + subject.modify_description("test", "Modified Long") + end + end + + describe "#target" do + it "obtains the zone target" do + allow(api).to receive(:string_command) + .with("--zone=test", "--get-target", permanent: !api.offline?) + .and_return("ACCEPT") + + expect(subject.target("test")).to eql("ACCEPT") + end + end + + describe "#modify_target" do + it "modifies the zone target" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--set-target=drop", permanent: !api.offline?) + + subject.modify_target("test", "drop") + end + end + describe "#masquerade_enabled?" do + it "returns false if the zone has masquerade disabled" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-masquerade", permanent: api.permanent?) + .and_return(false) + expect(subject.masquerade_enabled?("public")).to eql(false) + end + + it "returns true if the zone has masquerade enabled" do + allow(api).to receive(:query_command) + .with("--zone=external", "--query-masquerade", permanent: api.permanent?) + .and_return(true) + + expect(subject.masquerade_enabled?("external")).to eql(true) + end + end + + describe "#list_ports" do + it "returns the list of ports used by the zone" do + allow(api).to receive(:string_command) + .with("--zone=test", "--list-ports", permanent: api.permanent?) + .and_return("80/tcp 443/tcp") + + expect(api.list_ports("test")).to eql(["80/tcp", "443/tcp"]) + end + end + + describe "#list_protocols" do + it "returns the list of protocols used by the zone" do + allow(api).to receive(:string_command) + .with("--zone=test", "--list-protocols", permanent: api.permanent?) + .and_return("egp gre") + + expect(api.list_protocols("test")).to eql(["egp", "gre"]) + end + end + + describe "#list_sources" do + it "returns the list of sources binded to the zone" do + allow(api).to receive(:string_command) + .with("--zone=test", "--list-sources", permanent: api.permanent?) + .and_return("192.168.5.0/24") + + expect(api.list_sources("test")).to eql(["192.168.5.0/24"]) + end + end + + describe "#add_source" do + it "binds the given source with the zone" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--add-source=192.168.4.0/24", permanent: api.permanent?) + + api.add_source("test", "192.168.4.0/24") + end + end + + describe "#remove_source" do + it "unbinds the given source from the zone" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--remove-source=192.168.4.0/24", permanent: api.permanent?) + + api.remove_source("test", "192.168.4.0/24") + end + end + + describe "#source_enabled?" do + it "returns false if the source is not binded to the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-source=192.168.4.0/24", permanent: api.permanent?) + .and_return(false) + expect(subject.source_enabled?("public", "192.168.4.0/24")).to eql(false) + end + + it "returns true if the souce is binded by the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-source=192.168.4.0/24", permanent: api.permanent?) + .and_return(true) + expect(subject.source_enabled?("public", "192.168.4.0/24")).to eql(true) + end + end + + describe "#interface_zone" do + it "returns the name of the zone the interface belongs to" do + allow(api).to receive(:string_command) + .with("--get-zone-of-interface=eth0", permanent: api.permanent?) + .and_return("public") + + expect(api.interface_zone("eth0")).to eql("public") + end + end + + describe "#interface_enabled?" do + it "returns false if the interface is not binded to the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-interface=eth0", permanent: !api.offline?) + .and_return(false) + expect(subject.interface_enabled?("public", "eth0")).to eql(false) + end + + it "returns false if the interface is binded to the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-interface=eth1", permanent: !api.offline?) + .and_return(true) + expect(subject.interface_enabled?("public", "eth1")).to eql(true) + end + end + + describe "#add_interface" do + it "binds the given interface to the zone" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--add-interface=eth0", permanent: api.permanent?) + + api.add_interface("test", "eth0") + end + end + + describe "#remove_interface" do + it "unbinds the given interface from the zone" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--remove-interface=eth0", permanent: api.permanent?) + + api.remove_interface("test", "eth0") + end + end + + describe "#remove_port" do + it "removes the given port from the zone configured ports" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--remove-port=80/tcp", permanent: api.permanent?) + + api.remove_port("test", "80/tcp") + end + end + + describe "#add_port" do + it "adds the given port to the zone configured ports" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--add-port=80/tcp", permanent: api.permanent?) + + api.add_port("test", "80/tcp") + end + end + + describe "#port_enabled?" do + it "returns false if the port is not allowed by the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-port=80/tcp", permanent: !api.offline?) + .and_return(false) + expect(subject.port_enabled?("public", "80/tcp")).to eql(false) + end + + it "returns true if the port is allowed by the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-port=22/tcp", permanent: !api.offline?) + .and_return(true) + expect(subject.port_enabled?("public", "22/tcp")).to eql(true) + end + end + + describe "#protocol_enabled?" do + it "returns false if the protocol is not allowed by the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-protocol=igmp", permanent: api.permanent?) + .and_return(false) + expect(subject.protocol_enabled?("public", "igmp")).to eql(false) + end + + it "returns true if the protocol is allowed by the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-protocol=gre", permanent: api.permanent?) + .and_return(true) + expect(subject.protocol_enabled?("public", "gre")).to eql(true) + end + end + + describe "#service_enabled?" do + it "returns false if the service is not allowed by the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-service=samba", permanent: api.permanent?) + .and_return(false) + expect(subject.service_enabled?("public", "samba")).to eql(false) + end + + it "returns true if the service is allowed by the zone" do + allow(api).to receive(:query_command) + .with("--zone=public", "--query-service=http", permanent: api.permanent?) + .and_return(true) + expect(subject.service_enabled?("public", "http")).to eql(true) + end + end +end diff --git a/library/network/test/y2firewall/firewalld/api_test.rb b/library/network/test/y2firewall/firewalld/api_test.rb new file mode 100755 index 000000000..3aa7dd181 --- /dev/null +++ b/library/network/test/y2firewall/firewalld/api_test.rb @@ -0,0 +1,247 @@ +#!/usr/bin/env rspec +# encoding: utf-8 +# +# Copyright (c) 2018 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_relative "../../test_helper" +require "y2firewall/firewalld/api" + +describe Y2Firewall::Firewalld::Api do + subject(:api) { described_class.new(mode: :offline) } + + describe ".initialize" do + let(:running?) { false } + before do + allow_any_instance_of(described_class).to receive(:running?).and_return(running?) + end + + context "when no option is given" do + subject(:api) { described_class.new } + + context "and the firewall is running" do + let(:running?) { true } + + it "sets the API mode as :running" do + expect(api.offline?).to eq(false) + end + + it "sets the configuration to be read and written permanently" do + expect(api.permanent?).to eq(true) + end + end + + context "and the firewall is not running" do + let(:running?) { false } + + it "sets the API mode as :offline" do + expect(api.offline?).to eq(true) + end + end + end + + context "when the API mode is given" do + it "sets the mode" do + expect(described_class.new(mode: :offline).offline?).to eq(true) + expect(described_class.new(mode: :running).offline?).to eq(false) + end + end + end + + describe "#offline?" do + subject(:api) { described_class.new(mode: :offline) } + + it "returns true if the api is in :offline mode " do + expect(api.offline?).to eql(true) + end + + it "returns false otherwise" do + api.instance_variable_set("@mode", ":running") + expect(api.offline?).to eql(false) + end + end + + describe "#permanent?" do + subject(:api) { described_class.new(mode: :offline) } + + context "when the API is in :offline mode" do + it "returns false" do + expect(api.permanent?).to eql(false) + end + end + + context "when the API is in :running mode" do + it "returns true if the configuration should be written permanently" do + expect(described_class.new(mode: :running, permanent: true).permanent?).to eql(true) + end + + it "returns false if the configuration should be written only in runtime" do + expect(described_class.new(mode: :running, permanent: false).permanent?).to eql(false) + end + end + end + + describe "#running?" do + let(:package) { Y2Firewall::Firewalld::Api::PACKAGE } + let(:state) { "running" } + + before do + allow(Yast::Stage).to receive(:initial).and_return(false) + allow(Yast::PackageSystem).to receive(:Installed).with(package).and_return(true) + allow(api).to receive(:state).and_return(state) + end + + it "returns false during the installation first stage" do + allow(Yast::Stage).to receive(:initial).and_return(true) + + expect(api.running?).to eql(false) + end + + it "returns false if the firewalld package is not installed" do + allow(Yast::PackageSystem).to receive(:Installed).with(package).and_return(false) + + expect(api.running?).to eql(false) + end + + it "returns whether the firewalld state is 'running' or not" do + allow(api).to receive(:state).and_return("not_running", "running") + expect(api.running?).to eql(false) + expect(api.running?).to eql(true) + end + end + + describe "#enable!" do + context "when the firewall is not running" do + subject(:api) { described_class.new(mode: :offline) } + + it "enables the firewalld service through the firewalld offline cmd API" do + expect(api).to receive(:run_command).with("--enable") + + api.enable! + end + end + + context "when the firewall is running" do + subject(:api) { described_class.new(mode: :running) } + + it "enables the firewalld service through the Yast::Service module " do + expect(Yast::Service).to receive(:Enable).with("firewalld") + + api.enable! + end + end + end + + describe "#state" do + it "returns 'running' when the firewall is running" do + allow(Yast::Execute).to receive(:on_target) + .with("firewall-cmd", "--state", allowed_exitstatus: [0, 252]).and_return(0) + + expect(api.state).to eql("running") + end + + it "returns 'not running' when the firewall is not running" do + allow(Yast::Execute).to receive(:on_target) + .with("firewall-cmd", "--state", allowed_exitstatus: [0, 252]).and_return(252) + + expect(api.state).to eql("not running") + end + + it "returns 'unknown' in case of an unexpected state" do + allow(Yast::Execute).to receive(:on_target) + .with("firewall-cmd", "--state", allowed_exitstatus: [0, 252]).and_return(24) + + expect(api.state).to eql("unknown") + end + end + + describe "#log_denied_packets" do + before do + allow(api).to receive(:string_command).with("--get-log-denied").and_return("all") + end + + it "returns the kind of packets to be logged" do + expect(api.log_denied_packets).to eql("all") + end + end + + describe "#modify_log_denied_packets" do + it "modifies the kind of packets to be logged" do + expect(api).to receive(:string_command).with("--set-log-denied=broadcast", permanent: false) + api.modify_log_denied_packets("broadcast") + end + end + + describe "#default_zone" do + before do + allow(api).to receive(:string_command).with("--get-default-zone").and_return("drop") + end + + it "returns the current firewalld default zone" do + expect(api.default_zone).to eql("drop") + end + end + + describe "#modify_default_zone" do + it "modifies the firewalld default zone" do + expect(api).to receive(:string_command).with("--set-default-zone=external", permanent: false) + api.modify_default_zone("external") + end + end + + describe "#reload" do + context "when the firewall is not running" do + it "returns true" do + expect(api.reload).to eql(true) + end + end + + context "when the firewall is running" do + subject(:api) { described_class.new(mode: :running) } + + it "reloads the firewalld configuration" do + expect(api).to receive(:modify_command).with("--reload") + api.reload + end + + it "returns whether the configuration was reloaded successfully" do + allow(api).to receive(:modify_command).with("--reload").and_return(true) + + expect(api.reload).to eql(true) + end + end + end + + describe "#complete_reload" do + context "when the firewall is not running" do + it "returns true" do + expect(api.complete_reload).to eql(true) + end + end + + context "when the firewall is running" do + subject(:api) { described_class.new(mode: :running) } + + it "reloads the firewalld configuration" do + expect(api).to receive(:modify_command).with("--complete-reload") + api.complete_reload + end + end + end +end diff --git a/library/network/test/y2firewall/firewalld/service_reader_test.rb b/library/network/test/y2firewall/firewalld/service_reader_test.rb new file mode 100755 index 000000000..5c9956bed --- /dev/null +++ b/library/network/test/y2firewall/firewalld/service_reader_test.rb @@ -0,0 +1,78 @@ +#!/usr/bin/env rspec +# encoding: utf-8 +# +# Copyright (c) [2018] 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_relative "../../test_helper" +require "y2firewall/firewalld" +require "y2firewall/firewalld/service_reader" + +describe Y2Firewall::Firewalld::ServiceReader do + let(:firewalld) { Y2Firewall::Firewalld.instance } + let(:api) { instance_double("Y2Firewall::Firewalld::Api", state: "not_running") } + + before do + allow(firewalld).to receive(:api).and_return(api) + `echo true` + end + + describe "#read" do + let(:service_info) do + [ + "radius", + " summary: My service", + " description: This is my service description", + " ports: 137/tcp 137/udp", + " protocols: igmp", + " source-ports: ", + " modules: nf_conntrack_netbios_ns", + " destination: " + ] + end + + context "when the service is not present" do + let(:service_name) { "not_present" } + before do + allow(api).to receive(:info_service).with(service_name) + allow($CHILD_STATUS).to receive(:exitstatus).and_return(101) + end + + it "raises a NotFound exception" do + expect { subject.read(service_name) }.to raise_error(Y2Firewall::Firewalld::Service::NotFound) + end + end + + context "when the service configuration exists" do + let(:service_name) { "radius" } + before do + allow(api).to receive(:info_service).with(service_name) + .and_return(service_info) + allow($CHILD_STATUS).to receive(:exitstatus).and_return(1) + end + + it "returns the service with the parsed configuration" do + service = subject.read(service_name) + expect(service.short).to eq("My service") + expect(service.ports).to eq(["137/tcp", "137/udp"]) + expect(service.protocols).to eq(["igmp"]) + end + end + end +end diff --git a/library/network/test/y2firewall/firewalld/service_test.rb b/library/network/test/y2firewall/firewalld/service_test.rb new file mode 100755 index 000000000..a632158fd --- /dev/null +++ b/library/network/test/y2firewall/firewalld/service_test.rb @@ -0,0 +1,204 @@ +#!/usr/bin/env rspec +# encoding: utf-8 +# +# Copyright (c) [2018] 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_relative "../../test_helper" +require "y2firewall/firewalld" +require "y2firewall/firewalld/service" + +describe Y2Firewall::Firewalld::Service do + let(:firewalld) { Y2Firewall::Firewalld.instance } + let(:api) { Y2Firewall::Firewalld::Api.new(mode: :offline) } + let(:installed?) { true } + let(:service) { described_class.new(name: "service") } + + before do + allow(firewalld).to receive(:find_service).with("service") + allow(firewalld).to receive(:api).and_return(api) + allow(firewalld).to receive(:installed?).and_return(installed?) + end + + def mock_read_service + allow(api).to receive(:service_short).and_return("Test service") + allow(api).to receive(:service_description).and_return("Test service long description") + allow(api).to receive(:service_ports).and_return(["80/tcp", "53/udp"]) + allow(api).to receive(:service_protocols).and_return(["gre", "igmp"]) + allow(api).to receive(:service_supported?).with(service.name).and_return(true) + end + + describe ".modify_ports" do + subject { described_class } + + context "when firewalld is not installed" do + let(:installed?) { false } + + it "returns false" do + expect(subject.modify_ports(name: "service", tcp_ports: ["80", "8080"])).to eq(false) + end + end + + context "when firewalld is installed" do + before do + allow(service).to receive(:ports=) + allow(service).to receive(:apply_changes!) + allow(firewalld).to receive(:find_service).with("service").and_return(service) + end + + it "looks for the the service with the name given if exists" do + expect(firewalld).to receive(:find_service).with("service").and_return(service) + + subject.modify_ports(name: "service", tcp_ports: ["80"]) + end + + it "modifies the service tcp and udp ports" do + expect(service).to receive(:ports=).with(["80/tcp", "8080/tcp", "53/udp"]) + expect(service).to receive(:apply_changes!) + + subject.modify_ports(name: "service", tcp_ports: ["80", "8080"], udp_ports: ["53"]) + end + end + end + + describe "#create!" do + it "creates a new service definition for this service" do + expect(api).to receive(:create_service).with(service.name) + + service.create! + end + end + + describe "#supported?" do + it "returns true if a service definition for the service name exists" do + expect(api).to receive(:service_supported?).with(service.name).and_return(true) + + expect(service.supported?).to eql(true) + end + + it "returns false if there is no service definition for this service" do + new_service = described_class.new(name: "new_service") + expect(api).to receive(:service_supported?).with("new_service").and_return(false) + expect(new_service.supported?).to eql(false) + end + end + + describe "#read" do + before do + mock_read_service + end + + it "returns false if the service is not supported" do + allow(api).to receive(:service_supported?).with(service.name).and_return(false) + + expect(service.read).to eql(false) + end + + it "initializes the service using the api for each attribute or relation" do + service.read + expect(service.tcp_ports).to eql(["80"]) + expect(service.short).to eql("Test service") + expect(service.description).to eql("Test service long description") + end + + it "marks the service as not modified once read" do + service.read + expect(service.modified?).to eql(false) + end + + it "returns true when read" do + expect(service.read).to eql(true) + end + end + + describe "#apply_changes!" do + let(:modified) { true } + + before do + allow(service).to receive(:modified?).and_return(modified) + allow(service).to receive(:apply_attributes_changes!) + allow(service).to receive(:apply_relations_changes!) + end + + context "when the service has been modified" do + it "returns false if the service is not defined yet" do + allow(api).to receive(:service_supported?).with(service.name).and_return(false) + expect(service.apply_changes!).to eql(false) + end + + it "writes the modified services attributes" do + mock_read_service + service.read + service.short = "short Modified" + service.add_port("137/tcp") + service.add_protocol("bgp") + + expect(service).to receive(:apply_attributes_changes!) + expect(service).to receive(:apply_relations_changes!) + service.apply_changes! + end + + it "marks the service as not modified once written" do + allow(service).to receive(:modified?).and_call_original + mock_read_service + service.read + service.short = "short Modified" + expect(service.modified?).to eql(true) + service.apply_changes! + expect(service.modified?).to eql(false) + end + end + + context "when the service has not been modified" do + let(:modified) { false } + + it "does not do any API call" do + expect(service).to_not receive(:api) + + service.apply_changes! + end + + it "returns true" do + expect(service.apply_changes!).to eql(true) + end + end + end + + describe "#tcp_ports" do + before do + mock_read_service + end + + it "returns a list with the allowed tcp ports" do + service.read + expect(service.tcp_ports).to eql(["80"]) + end + end + + describe "#udp_ports" do + before do + mock_read_service + end + + it "returns a list with the allowed udp ports" do + service.read + expect(service.udp_ports).to eql(["53"]) + end + end +end diff --git a/library/network/test/y2firewall/firewalld/zone_parser.rb b/library/network/test/y2firewall/firewalld/zone_reader_test.rb old mode 100644 new mode 100755 similarity index 69% rename from library/network/test/y2firewall/firewalld/zone_parser.rb rename to library/network/test/y2firewall/firewalld/zone_reader_test.rb index 721c07b1e..e11e4b794 --- a/library/network/test/y2firewall/firewalld/zone_parser.rb +++ b/library/network/test/y2firewall/firewalld/zone_reader_test.rb @@ -1,7 +1,7 @@ #!/usr/bin/env rspec # encoding: utf-8 # -# Copyright (c) [2017] SUSE LLC +# Copyright (c) 2018 SUSE LLC # # All Rights Reserved. # @@ -21,10 +21,10 @@ # find current contact information at www.suse.com. require_relative "../../test_helper" -require "y2firewall/firewalld/zone_parser" +require "y2firewall/firewalld/zone_reader" -describe Y2Firewall::Firewalld::ZoneParser do - describe "#parse" do +describe Y2Firewall::Firewalld::ZoneReader do + describe "#read" do subject { described_class.new(zone_names, zones_definition) } let(:zone_names) { ["public", "dmz"] } let(:zones_definition) do @@ -45,18 +45,26 @@ "\t", "", "public (active)", + " summary: Public", + " description: For use in public areas. You do not trust the other" \ + " computers on networks to not harm your computer." \ + " Only selected incoming connections are accepted.", " target: default", " icmp-block-inversion: no", " interfaces: eth0 ens3", - " sources: ", + " sources: 192.168.0.0/24 192.168.1.0/24 192.168.2.0/24", " services: ssh iscsi-target", " ports: 123/udp 21/udp 111/tcp 111/udp 1123/udp 530/udp 530/tcp", " protocols: ", " masquerade: yes", - " forward-ports: ", + " forward-ports: port=2222:proto=tcp:toport=22:toaddr=", + " port=9080:proto=tcp:toport=80:toaddr=", " source-ports: ", - " icmp-blocks: ", + " icmp-blocks: echo-request echo-reply", " rich rules: ", + " rule service name=\"http\" accept", + " rule service name=\"https\" accept", + " rule service name=\"ssh\" accept", "\t" ] end @@ -65,27 +73,29 @@ let(:zone_names) { [] } it "returns an empty array" do - expect(subject.parse).to eq([]) + expect(subject.read).to eq([]) end end context "when some zone is configured" do it "returns an array of Y2Firewall::Firewalld::Zone" do - zones = subject.parse + zones = subject.read expect(zones.size).to eq(2) expect(zones).to all(be_an(Y2Firewall::Firewalld::Zone)) end it "initializes each zone based on the zone definition" do - zones = subject.parse + zones = subject.read public_zone = zones.find { |z| z.name == "public" } - + expect(public_zone.target).to eq("default") + expect(public_zone.short).to eq("Public") + expect(public_zone.description).to include("You do not trust the other computers") expect(public_zone.services).to eq(["ssh", "iscsi-target"]) expect(public_zone.interfaces).to eq(["eth0", "ens3"]) expect(public_zone.ports).to include("123/udp", "530/udp") expect(public_zone.masquerade).to eq(true) - + expect(public_zone.sources).to eq(["192.168.0.0/24", "192.168.1.0/24", "192.168.2.0/24"]) dmz_zone = zones.find { |z| z.name == "dmz" } expect(dmz_zone.masquerade).to eq(false) expect(dmz_zone.services).to be_empty diff --git a/library/network/test/y2firewall/firewalld/zone_test.rb b/library/network/test/y2firewall/firewalld/zone_test.rb old mode 100644 new mode 100755 index 684f4a03d..972c274a9 --- a/library/network/test/y2firewall/firewalld/zone_test.rb +++ b/library/network/test/y2firewall/firewalld/zone_test.rb @@ -1,7 +1,7 @@ #!/usr/bin/env rspec # encoding: utf-8 # -# Copyright (c) [2017] SUSE LLC +# Copyright (c) 2018 SUSE LLC # # All Rights Reserved. # @@ -22,21 +22,16 @@ require_relative "../../test_helper" require "y2firewall/firewalld" +require "y2firewall/firewalld/api" require "y2firewall/firewalld/zone" describe Y2Firewall::Firewalld::Zone do let(:firewalld) { Y2Firewall::Firewalld.instance } + let(:api) { instance_double("Y2Firewall::Firewalld::Api", default_zone: "default") } before do allow(firewalld).to receive(:installed?).and_return(true) - end - - describe ".known_zones" do - it "returns a hash with known zone names and descriptions" do - expect(described_class.known_zones).to be_a(Hash) - expect(described_class.known_zones).to include "public" - expect(described_class.known_zones["dmz"]).to eq(N_("Demilitarized Zone")) - end + allow(firewalld).to receive(:api).and_return(api) end describe "#initialize" do @@ -48,11 +43,7 @@ end context "when :name is not specified" do - let(:api) { instance_double("Y2Firewall::Firewalld::Api", default_zone: "default") } - it "uses the default zone name" do - allow(firewalld).to receive(:api).and_return(api) - expect(subject.name).to eq("default") end end @@ -64,11 +55,12 @@ before do allow(firewalld).to receive(:api).and_return(api) + subject.relations.each do |r| + allow(subject).to receive(:public_send).with("current_#{r}").and_return([]) + end allow(subject).to receive(:public_send).with("current_services").and_return(["ssh"]) allow(subject).to receive(:public_send).with("current_interfaces").and_return(["eth0", "eth1"]) allow(subject).to receive(:public_send).with("current_ports").and_return(["80/tcp", "443/tcp"]) - allow(subject).to receive(:public_send).with("current_protocols").and_return([]) - allow(subject).to receive(:public_send).with("current_sources").and_return([]) allow(subject).to receive(:public_send).with(:interfaces).and_call_original end @@ -92,6 +84,14 @@ end end + describe "#reload!" do + it "forces a reload of the firewalld configuration" do + expect(api).to receive(:reload) + + subject.reload! + end + end + describe "#export" do subject { described_class.new(name: "test") } @@ -128,4 +128,83 @@ expect(subject.interfaces).to eq(["eth0", "eth1"]) end end + + describe "#add_interface!" do + subject { described_class.new(name: "test") } + + it "calls the API changing the specified interface to this zone" do + expect(api).to receive(:change_interface).with("test", "eth0") + + subject.add_interface!("eth0") + end + end + + describe "#add_source!" do + subject { described_class.new(name: "test") } + + it "calls the API changing the specified source to this zone" do + expect(api).to receive(:change_source).with("test", "192.168.1.0/24") + + subject.add_source!("192.168.1.0/24") + end + end + + describe "#service_open?" do + it "returns whether the service is allowed or not in the zone" do + allow(subject).to receive(:services).and_return(["ssh", "vnc"]) + + expect(subject.service_open?("ssh")).to eql(true) + expect(subject.service_open?("samba")).to eql(false) + end + end + + describe "#full_name" do + subject { described_class.new(name: "block") } + + it "returns the zone known full name" do + expect(subject.full_name).to eq("Block Zone") + end + end + + describe "#apply_changes!" do + context "when the zone has not been modified" do + it "returns true" do + allow(subject).to receive(:modified?).and_return(false) + expect(subject.apply_changes!).to eql(true) + end + end + + context "when the zone has been modified" do + subject { described_class.new(name: "test") } + + it "applies all the changes done in its relations" do + subject.services = ["ssh"] + expect(subject).to receive(:apply_relations_changes!) + subject.apply_changes! + end + + it "applies all the changes done in its attributes" do + subject.target = "ACCEPT" + expect(subject).to receive(:apply_attributes_changes!) + subject.apply_changes! + end + + it "applies the masquerading modifications if it was modified" do + expect(api).to receive(:add_masquerade) + subject.masquerade = true + subject.apply_changes! + end + + it "sets the zone as not modified once applied all the changes" do + subject.modified!(:false_value) + expect(subject.modified?).to eql(true) + subject.apply_changes! + expect(subject.modified?).to eql(false) + end + + it "returns true when applied all the changes" do + expect(subject.apply_changes!).to eql(true) + end + end + end end diff --git a/library/network/test/y2firewall/firewalld_test.rb b/library/network/test/y2firewall/firewalld_test.rb old mode 100644 new mode 100755 index e48732ffa..e723085f0 --- a/library/network/test/y2firewall/firewalld_test.rb +++ b/library/network/test/y2firewall/firewalld_test.rb @@ -1,7 +1,7 @@ #!/usr/bin/env rspec # encoding: utf-8 # -# Copyright (c) [2017] SUSE LLC +# Copyright (c) 2018 SUSE LLC # # All Rights Reserved. # @@ -27,11 +27,21 @@ Yast.import "Service" describe Y2Firewall::Firewalld do - let(:firewalld) { described_class.instance } - let(:known_zones) { Y2Firewall::Firewalld::Zone.known_zones.keys } + let(:firewalld) { described_class.clone.instance } + let(:known_zones) { %w(dmz drop external home internal public trusted work) } + let(:known_services) { %w(http https samba ssh) } let(:empty_zones) { known_zones.map { |z| Y2Firewall::Firewalld::Zone.new(name: z) } } + let(:installed?) { true } + before do + allow(firewalld).to receive(:installed?).and_return(installed?) + allow_any_instance_of(Y2Firewall::Firewalld::Api).to receive(:running?).and_return(false) + end describe "#installed?" do + before do + allow(firewalld).to receive(:installed?).and_call_original + end + it "returns false it the firewalld is not installed" do allow(Yast::PackageSystem).to receive("Installed") .with(described_class::PACKAGE).and_return(false) @@ -48,6 +58,10 @@ end describe "#enabled?" do + before do + allow(firewalld).to receive(:installed?).and_return(true) + end + it "returns true if the firewalld service is enable" do allow(Yast::Service).to receive("Enabled") .with(described_class::SERVICE).and_return(true) @@ -172,7 +186,7 @@ it "returns true if the service is running" do expect(firewalld.api).to receive(:running?).and_return(true) - firewalld.running? + expect(firewalld.running?).to eq(true) end end @@ -205,7 +219,8 @@ log_denied_packets: "off", default_zone: "dmz", list_all_zones: zones_definition, - zones: known_zones) + zones: known_zones, + services: known_services) end before do @@ -218,6 +233,14 @@ expect(firewalld.read).to eq(false) end + it "stores the list of available zone names" do + expect { firewalld.read }.to change { firewalld.current_zone_names }.from([]).to(known_zones) + end + + it "stores the list of available service names" do + expect { firewalld.read }.to change { firewalld.current_service_names }.from([]).to(known_services) + end + it "initializes the list of zones parsing the firewalld summary" do firewalld.read @@ -251,18 +274,21 @@ describe "#modified?" do let(:api) do - instance_double(Y2Firewall::Firewalld::Api, log_denied_packets: "off", default_zone: "public") + instance_double(Y2Firewall::Firewalld::Api, log_denied_packets: "off", + default_zone: "public", zones: known_zones, services: ["http"]) end let(:modified_zone) { false } before do allow(firewalld).to receive("api").and_return api + firewalld.zones = empty_zones + firewalld.current_zone_names = known_zones empty_zones.each do |zone| allow(zone).to receive(:modified?).and_return(modified_zone) end - firewalld.zones = empty_zones firewalld.log_denied_packets = "all" + firewalld.untouched! end context "when some of the attributes have been modified since read" do @@ -272,10 +298,8 @@ end end - context "when no attribute has been modifiede since read" do + context "when no attribute has been modified since read" do it "returns false" do - firewalld.default_zone = "public" - firewalld.log_denied_packets = "off" expect(firewalld.modified?).to eq(false) end end @@ -285,17 +309,23 @@ let(:api) do Y2Firewall::Firewalld::Api.new end + let(:api) do + instance_double(Y2Firewall::Firewalld::Api, log_denied_packets: "on", + default_zone: "public", zones: known_zones, services: ["http"]) + end before do - allow(firewalld).to receive("read?").and_return(true) firewalld.zones = empty_zones - allow(firewalld).to receive("api").and_return api + firewalld.current_zone_names = known_zones empty_zones.each do |zone| allow(zone).to receive(:modified?).and_return(false) end - allow(api).to receive(:default_zone=) - allow(api).to receive(:log_denied_packets=) + allow(firewalld).to receive("read?").and_return(true) + allow(firewalld).to receive("api").and_return api + allow(firewalld).to receive(:apply_zones_changes!) + allow(api).to receive(:modify_default_zone) + allow(api).to receive(:modify_log_denied_packets) end it "enforces a read of the configuration if not read before" do @@ -309,13 +339,14 @@ firewalld.log_denied_packets = "off" firewalld.default_zone = "drop" - expect(api).to receive(:default_zone=).with("drop") - expect(api).to receive(:log_denied_packets=).with("off") + expect(api).to receive(:modify_default_zone).with("drop") + expect(api).to receive(:modify_log_denied_packets).with("off") firewalld.write_only end it "only apply changes to the modified zones" do + expect(firewalld).to receive(:apply_zones_changes!).and_call_original dmz = firewalld.find_zone("dmz") allow(dmz).to receive(:modified?).and_return(true) expect(dmz).to receive(:apply_changes!) @@ -369,11 +400,11 @@ log_denied_packets: "all", default_zone: "work", list_all_zones: zones_definition, - zones: known_zones) + zones: known_zones, + services: known_services) end before do - allow(firewalld).to receive("api").and_return api allow(firewalld).to receive("api").and_return api allow(firewalld).to receive("running?").and_return true allow(firewalld).to receive("enabled?").and_return false diff --git a/library/network/test/y2firewall/helpers/interfaces_test.rb b/library/network/test/y2firewall/helpers/interfaces_test.rb old mode 100644 new mode 100755 diff --git a/library/network/test/y2firewall/relations_test.rb b/library/network/test/y2firewall/relations_test.rb new file mode 100644 index 000000000..965fe2e6f --- /dev/null +++ b/library/network/test/y2firewall/relations_test.rb @@ -0,0 +1,96 @@ +#!/usr/bin/env rspec +# encoding: utf-8 +# +# Copyright (c) 2018 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_relative "../test_helper" +require "y2firewall/firewalld" + +describe Y2Firewall::Firewalld::Relations do + class Dummy + extend Y2Firewall::Firewalld::Relations + attr_accessor :api + + has_attributes :description + has_many :dummies + + enable_modifications_cache + end + + describe ".enable_modifications_cache" do + subject { Dummy.new } + + it "defines the 'modified' method" do + expect(subject.modified).to be_an(Array) + end + + it "defines the 'modified!' method" do + expect(subject.modified?).to eq(false) + subject.modified! :name + expect(subject.modified?).to eq(true) + end + + it "defines the 'untouched!' method" do + subject.untouched! + expect(subject.modified?).to eq(false) + end + + it "defines the 'modified?' method" do + subject.modified! :name + expect(subject.modified?(:name)).to eq(true) + end + end + + describe ".has_attributes" do + subject { Dummy.new } + + let(:api) do + instance_double("Y2Firewall::Firewalld::API", + description: "dummy text", dummies: ["john", "doe"]) + end + + before { allow(subject).to receive(:api).and_return(api) } + + it "defines a getter and a setter for each given attribute" do + expect(subject.respond_to?("description")).to eq(true) + expect(subject.respond_to?("description=")).to eq(true) + end + + it "defines the 'attributes' method" do + expect(subject.attributes).to eq([:description]) + end + + it "defines the \"current_'attribute'\" method" do + subject.current_description + end + + it "defines the 'read_attributes' method" do + subject.read_attributes + expect(subject.description).to eq("dummy text") + end + + it "defines the 'apply_attributes_changes!' method" do + subject.description = "modified dummy text" + expect(api).to receive(:public_send).with("modify_description", "modified dummy text") + + subject.apply_attributes_changes! + end + end +end diff --git a/library/network/testsuite/tests/NO/SuSEFirewall.err b/library/network/testsuite/tests/NO/SuSEFirewall.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/library/network/testsuite/tests/NO/SuSEFirewall.out b/library/network/testsuite/tests/NO/SuSEFirewall.out deleted file mode 100644 index 5c168b630..000000000 --- a/library/network/testsuite/tests/NO/SuSEFirewall.out +++ /dev/null @@ -1,84 +0,0 @@ -Dump == SuSEfirewall2 service == -Return false -Return false -Return nil -Return nil -Return true -Return true -Dump -Dump == Read/Write == -Return true -Return true -Dump -Dump == Import/Export == -Return nil -Return $["FW_ALLOW_FW_BROADCAST_DMZ":"no", "FW_ALLOW_FW_BROADCAST_EXT":"no", "FW_ALLOW_FW_BROADCAST_INT":"no", "FW_BOOT_FULL_INIT":"no", "FW_CONFIGURATIONS_DMZ":"aaa-bbb", "FW_CONFIGURATIONS_EXT":"aaa-bbb ab-ab-ab aa-aa-aa bb-bb-bb", "FW_CONFIGURATIONS_INT":"", "FW_DEV_DMZ":"", "FW_DEV_EXT":"eth6 special-string eth8", "FW_DEV_INT":"dsl0", "FW_FORWARD_ALWAYS_INOUT_DEV":"", "FW_FORWARD_MASQ":"", "FW_IGNORE_FW_BROADCAST_DMZ":"no", "FW_IGNORE_FW_BROADCAST_EXT":"yes", "FW_IGNORE_FW_BROADCAST_INT":"no", "FW_IPSEC_TRUST":"no", "FW_LOAD_MODULES":"\n\n\n", "FW_LOG_ACCEPT_ALL":"no", "FW_LOG_ACCEPT_CRIT":"yes", "FW_LOG_DROP_ALL":"no", "FW_LOG_DROP_CRIT":"yes", "FW_MASQUERADE":"no", "FW_PROTECT_FROM_INT":"no", "FW_ROUTE":"no", "FW_SERVICES_ACCEPT_DMZ":"", "FW_SERVICES_ACCEPT_EXT":"", "FW_SERVICES_ACCEPT_INT":"", "FW_SERVICES_ACCEPT_RELATED_DMZ":"", "FW_SERVICES_ACCEPT_RELATED_EXT":"", "FW_SERVICES_ACCEPT_RELATED_INT":"", "FW_SERVICES_DMZ_IP":"", "FW_SERVICES_DMZ_RPC":"", "FW_SERVICES_DMZ_TCP":"", "FW_SERVICES_DMZ_UDP":"", "FW_SERVICES_EXT_IP":"", "FW_SERVICES_EXT_RPC":"", "FW_SERVICES_EXT_TCP":"", "FW_SERVICES_EXT_UDP":"", "FW_SERVICES_INT_IP":"", "FW_SERVICES_INT_RPC":"", "FW_SERVICES_INT_TCP":"", "FW_SERVICES_INT_UDP":"", "FW_STOP_KEEP_ROUTING_STATE":"no", "enable_firewall":false, "start_firewall":false] -Dump -Dump == Firewall behaviour == -Return [$["id":"ippp5", "name":"", "type":"dialup", "zone":nil], $["id":"ppp5", "name":"", "type":"dialup", "zone":nil], $["id":"arc5", "name":"", "zone":nil], $["id":"atm5", "name":"", "zone":nil], $["id":"ci5", "name":"", "zone":nil], $["id":"ctc5", "name":"", "zone":nil], $["id":"dummy5", "name":"", "zone":nil], $["id":"escon5", "name":"", "zone":nil], $["id":"eth5", "name":"", "zone":nil], $["id":"eth6", "name":"", "zone":"EXT"], $["id":"eth7", "name":"", "zone":nil], $["id":"eth8", "name":"", "zone":"EXT"], $["id":"eth9", "name":"", "zone":nil], $["id":"fddi5", "name":"", "zone":nil], $["id":"hippi5", "name":"", "zone":nil], $["id":"hsi5", "name":"", "zone":nil], $["id":"iucv5", "name":"", "zone":nil], $["id":"myri5", "name":"", "zone":nil], $["id":"tr5", "name":"", "zone":nil]] -Return ["INT", "DMZ", "EXT"] -Dump -Dump == Interfaces Handling == -Return ["eth6", "eth8"] -Return nil -Return nil -Return ["eth6", "eth8", "eth9"] -Return ["special-string", "undefined"] -Return nil -Return nil -Return nil -Return ["eth8"] -Return ["eth9"] -Return ["special-string"] -Return nil -Return DMZ -Return ["DMZ", "INT"] -Dump -Dump == Logging settings == -Return CRIT -Return CRIT -Return nil -Return nil -Return ALL -Return NONE -Return no -Return yes -Return no -Return nil -Return nil -Return nil -Return yes -Return no -Return yes -Dump -Dump == Ports handling == -Return false -Return true -Return true -Return true -Return false -Dump -Dump == Broadcast handling == -Return $["DMZ":[], "EXT":[], "INT":[]] -Return nil -Return $["DMZ":["5", "3", "1"], "EXT":["22", "33", "44"], "INT":[]] -Dump -Dump == Masquerade handling == -Return false -Return nil -Return true -Dump -Dump == Service Stop / Start == -Return true -Return true -Return true -Return true -Dump -Dump == Additional Kernel Modules == -Return [] -Return nil -Return ["module_a", "module_b", "module_c"] -Return nil -Return ["module_x", "module_y", "module_z"] -Return nil -Return ["module_a", "module_b", "module_c", "module_x", "module_y", "module_z"] diff --git a/library/network/testsuite/tests/NO/SuSEFirewall.rb b/library/network/testsuite/tests/NO/SuSEFirewall.rb deleted file mode 100644 index a2a3ac6d6..000000000 --- a/library/network/testsuite/tests/NO/SuSEFirewall.rb +++ /dev/null @@ -1,556 +0,0 @@ -# encoding: utf-8 - -# *************************************************************************** -# -# Copyright (c) 2002 - 2012 Novell, Inc. -# 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 Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, -# you may find current contact information at www.novell.com -# -# *************************************************************************** -module Yast - class SuSEFirewallClient < Client - def main - Yast.include self, "testsuite.rb" - # testedfiles: SuSEFirewall - - @SuSEfirewall2 = { - "FW_ALLOW_FW_BROADCAST_DMZ" => "no", - "FW_ALLOW_FW_BROADCAST_EXT" => "no", - "FW_ALLOW_FW_BROADCAST_INT" => "no", - "FW_CONFIGURATIONS_DMZ" => "aaa-bbb", - "FW_CONFIGURATIONS_EXT" => "aaa-bbb ab-ab-ab aa-aa-aa bb-bb-bb", - "FW_CONFIGURATIONS_INT" => "", - "FW_DEV_DMZ" => "", - "FW_DEV_EXT" => "eth6 special-string eth8", - "FW_DEV_INT" => "dsl0", - "FW_FORWARD_MASQ" => "", - "FW_IGNORE_FW_BROADCAST_DMZ" => "no", - "FW_IGNORE_FW_BROADCAST_EXT" => "yes", - "FW_IGNORE_FW_BROADCAST_INT" => "no", - "FW_IPSEC_TRUST" => "no", - "FW_LOAD_MODULES" => "\n\n\n", - "FW_LOG_ACCEPT_ALL" => "no", - "FW_LOG_ACCEPT_CRIT" => "yes", - "FW_LOG_DROP_ALL" => "no", - "FW_LOG_DROP_CRIT" => "yes", - "FW_MASQUERADE" => "no", - "FW_PROTECT_FROM_INT" => "no", - "FW_ROUTE" => "no", - "FW_SERVICES_DMZ_IP" => "", - "FW_SERVICES_DMZ_RPC" => "", - "FW_SERVICES_DMZ_TCP" => "", - "FW_SERVICES_DMZ_UDP" => "", - "FW_SERVICES_EXT_IP" => "", - "FW_SERVICES_EXT_RPC" => "", - "FW_SERVICES_EXT_TCP" => "", - "FW_SERVICES_EXT_UDP" => "", - "FW_SERVICES_INT_IP" => "", - "FW_SERVICES_INT_RPC" => "", - "FW_SERVICES_INT_TCP" => "", - "FW_SERVICES_INT_UDP" => "", - "enable_firewall" => false, - "start_firewall" => false - } - - # data have been stolen from mvidner's testsuite 'NetworkDevices2.ycp' - @Network = { - "section" => { - "arc5" => nil, - "atm5" => nil, - "ci5" => nil, - "ctc5" => nil, - "dummy5" => nil, - "escon5" => nil, - "eth5" => nil, - "eth6" => nil, - "eth7" => nil, - "eth8" => nil, - "eth9" => nil, - "fddi5" => nil, - "hippi5" => nil, - "hsi5" => nil, - "ippp5" => nil, - "iucv5" => nil, - "lo" => nil, - "myri5" => nil, - "ppp5" => nil, - "tr5" => nil, - "tr~" => nil - }, - "value" => { - "arc5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "atm5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ci5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ctc5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "dummy5" => { - "BOOTPROTO" => "static", - "IPADDR" => "1.2.3.4", - "NETMASK" => "255.0.0.0", - "STARTMODE" => "manual" - }, - "escon5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "eth5" => - # "IPADDR_x":"1.1.1.1", "NETMASK_x":"0.0.0.0" - { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "eth6" => { - "BOOTPROTO" => "static", - "IPADDR" => "1.2.3.4", - "STARTMODE" => "manual" - }, - "eth7" => { "STARTMODE" => "manual" }, - "eth8" => { "IPADDR" => "1.2.3.4/8", "STARTMODE" => "manual" }, - "eth9" => { - "IPADDR" => "1.2.3.4", - "PREFIXLEN" => "8", - "STARTMODE" => "manual" - }, - "fddi5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "hippi5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "hsi5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ippp5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "iucv5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "lo" => - # "IPADDR_1":"7.7.7.7" - { - "BROADCAST" => "127.255.255.255", - "IPADDR" => "127.0.0.1", - "NETMASK" => "255.0.0.0", - "NETWORK" => "127.0.0.0", - "STARTMODE" => "onboot" - }, - "myri5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ppp5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "tr5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" } - } - } - - @READ = { - "sysconfig" => { "SuSEfirewall2" => @SuSEfirewall2 }, - "network" => @Network, - "probe" => { "system" => [] }, - "target" => { "tmpdir" => "/tmp" } - } - - @WRITE = {} - - @EXECUTE = { - "target" => { - "bash_output" => { - "exit" => 0, - "stdout" => "", - "stderr" => "" - }, - "bash" => 0 - } - } - - TESTSUITE_INIT([@READ, @WRITE, @EXECUTE], nil) - Yast.import "SuSEFirewall" - - # Configuration must be read! - SuSEFirewall.Read - # initialize to disabled, not running - SuSEFirewall.SetEnableService(false) - SuSEFirewall.SetStartService(false) - - DUMP("== SuSEfirewall2 service ==") - TEST(->() { SuSEFirewall.GetEnableService }, [@READ, @WRITE, @EXECUTE], nil) - TEST(->() { SuSEFirewall.GetStartService }, [@READ, @WRITE, @EXECUTE], nil) - TEST(->() { SuSEFirewall.SetEnableService(true) }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.SetStartService(true) }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetEnableService }, [@READ, @WRITE, @EXECUTE], nil) - TEST(->() { SuSEFirewall.GetStartService }, [@READ, @WRITE, @EXECUTE], nil) - - DUMP("") - DUMP("== Read/Write ==") - TEST(->() { SuSEFirewall.Read }, [@READ, @WRITE, @EXECUTE], nil) - TEST(->() { SuSEFirewall.Write }, [@READ, @WRITE, @EXECUTE], nil) - - DUMP("") - DUMP("== Import/Export ==") - TEST(->() { SuSEFirewall.Import(@SuSEfirewall2) }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.Export }, [@READ, @WRITE, @EXECUTE], nil) - - DUMP("") - DUMP("== Firewall behaviour ==") - TEST(->() { SuSEFirewall.GetAllKnownInterfaces }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetKnownFirewallZones }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - DUMP("") - DUMP("== Interfaces Handling ==") - TEST(->() { SuSEFirewall.GetInterfacesInZone("EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.AddInterfaceIntoZone("undefined", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.AddInterfaceIntoZone("eth9", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetInterfacesInZone("EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetSpecialInterfacesInZone("EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - TEST(->() { SuSEFirewall.AddInterfaceIntoZone("eth9", "DMZ") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.RemoveInterfaceFromZone("eth6", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.RemoveInterfaceFromZone("undefined", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetInterfacesInZone("EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetInterfacesInZone("DMZ") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetSpecialInterfacesInZone("EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - TEST(->() { SuSEFirewall.GetZoneOfInterface("-none-") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetZoneOfInterface("eth9") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetZonesOfInterfaces(["eth9", "dsl0"]) }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - DUMP("") - DUMP("== Logging settings ==") - TEST(->() { SuSEFirewall.GetLoggingSettings("ACCEPT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetLoggingSettings("DROP") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.SetLoggingSettings("ACCEPT", "ALL") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.SetLoggingSettings("DROP", "NONE") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetLoggingSettings("ACCEPT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetLoggingSettings("DROP") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - TEST(->() { SuSEFirewall.GetIgnoreLoggingBroadcast("INT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetIgnoreLoggingBroadcast("EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetIgnoreLoggingBroadcast("DMZ") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.SetIgnoreLoggingBroadcast("INT", "yes") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.SetIgnoreLoggingBroadcast("EXT", "no") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.SetIgnoreLoggingBroadcast("DMZ", "yes") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetIgnoreLoggingBroadcast("INT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetIgnoreLoggingBroadcast("EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetIgnoreLoggingBroadcast("DMZ") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - DUMP("") - DUMP("== Ports handling ==") - TEST(->() { SuSEFirewall.HaveService("www", "TCP", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.AddService("www", "TCP", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.HaveService("80", "TCP", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.RemoveService("www-http", "TCP", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.HaveService("80", "TCP", "EXT") }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - DUMP("") - DUMP("== Broadcast handling ==") - TEST(->() { SuSEFirewall.GetBroadcastAllowedPorts }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(lambda do - SuSEFirewall.SetBroadcastAllowedPorts( - "INT" => [], "DMZ" => ["5", "3", "1"], "EXT" => ["22", "33", "44"] - ) - end, [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetBroadcastAllowedPorts }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - DUMP("") - DUMP("== Masquerade handling ==") - TEST(->() { SuSEFirewall.GetMasquerade }, [@READ, @WRITE, @EXECUTE], nil) - TEST(->() { SuSEFirewall.SetMasquerade(true) }, [@READ, @WRITE, @EXECUTE], nil) - TEST(->() { SuSEFirewall.GetMasquerade }, [@READ, @WRITE, @EXECUTE], nil) - - @EXECUTE_OK = deep_copy(@EXECUTE) - if Ops.get_map(@EXECUTE_OK, "target", {}) == {} - Ops.set(@EXECUTE_OK, "target", {}) - end - Ops.set( - @EXECUTE_OK, - ["target", "bash_output"], - "exit" => 0, "stdout" => "Some warnings about IPv6", "stderr" => "" - ) - - @EXECUTE_ERR = deep_copy(@EXECUTE) - if Ops.get_map(@EXECUTE_ERR, "target", {}) == {} - Ops.set(@EXECUTE_ERR, "target", {}) - end - Ops.set( - @EXECUTE_ERR, - ["target", "bash_output"], - - "exit" => 35, - "stdout" => "Some warnings about IPv6", - "stderr" => "Some errors!" - - ) - - DUMP("") - DUMP("== Service Stop / Start ==") - TEST(->() { SuSEFirewall.StopServices }, [@READ, @WRITE, @EXECUTE_OK], nil) - TEST(->() { SuSEFirewall.StopServices }, [@READ, @WRITE, @EXECUTE_OK], nil) - TEST(->() { SuSEFirewall.StartServices }, [@READ, @WRITE, @EXECUTE_ERR], nil) - TEST(->() { SuSEFirewall.StartServices }, [@READ, @WRITE, @EXECUTE_ERR], nil) - - DUMP("") - DUMP("== Additional Kernel Modules ==") - TEST(->() { SuSEFirewall.GetFirewallKernelModules }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - # empty modules, nil modules - TEST(lambda do - SuSEFirewall.SetFirewallKernelModules( - ["module_a", nil, "module_b", "", "module_c", ""] - ) - end, [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetFirewallKernelModules }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(lambda do - SuSEFirewall.SetFirewallKernelModules( - ["module_z", "module_x", "module_y"] - ) - end, [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetFirewallKernelModules }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - # more modules in one entry - separated by space or tab - TEST(lambda do - SuSEFirewall.SetFirewallKernelModules( - ["module_a module_z", "module_y\tmodule_x", "module_b module_c"] - ) - end, [ - @READ, - @WRITE, - @EXECUTE - ], nil) - TEST(->() { SuSEFirewall.GetFirewallKernelModules }, - [ - @READ, - @WRITE, - @EXECUTE - ], nil) - - nil - end - end -end - -Yast::SuSEFirewallClient.new.main diff --git a/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.err b/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.out b/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.out deleted file mode 100644 index fda607452..000000000 --- a/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.out +++ /dev/null @@ -1,34 +0,0 @@ -Dump == IsValidNetwork == -Dump All these should be *valid* (true): -Return true -Return true -Return true -Return true -Return true -Return true -Return true -Return true -Dump All these should be *invalid* (false): -Return false -Return false -Return false -Return false -Dump Testing adding/reading expert rules -Return [] -Return true -Return [$["dport":"", "network":"192.168.0.1/255.255.240.0", "options":"hitcount=3,blockseconds=60,recentname=ssh", "protocol":"tcp", "sport":"22"]] -Return true -Return [$["dport":"", "network":"192.168.0.1/255.255.240.0", "options":"hitcount=3,blockseconds=60,recentname=ssh", "protocol":"tcp", "sport":"22"], $["dport":"", "network":"192.168.0.1/255.255.240.0", "options":"whatever=1", "protocol":"tcp", "sport":""]] -Return true -Return [$["dport":"", "network":"192.168.0.1/255.255.240.0", "options":"whatever=1", "protocol":"tcp", "sport":""]] -Dump Cannot remove rule that doesn't exist -Return false -Return [$["dport":"", "network":"192.168.0.1/255.255.240.0", "options":"whatever=1", "protocol":"tcp", "sport":""]] -Return true -Return [] -Dump Adding special rule allowed 'from all networks' -Return true -Return [$["dport":"", "network":"0/0", "options":"", "protocol":"udp", "sport":"888"]] -Return true -Return [$["dport":"", "network":"0/0", "options":"", "protocol":"udp", "sport":"888"], $["dport":"", "network":"0.0.0.0/0", "options":"", "protocol":"tcp", "sport":"999"]] -Dump == Done == diff --git a/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.rb b/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.rb deleted file mode 100644 index b3a1bf8bf..000000000 --- a/library/network/testsuite/tests/NO/SuSEFirewallExpertRules.rb +++ /dev/null @@ -1,142 +0,0 @@ -# encoding: utf-8 - -# *************************************************************************** -# -# Copyright (c) 2002 - 2012 Novell, Inc. -# 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 Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, -# you may find current contact information at www.novell.com -# -# *************************************************************************** -module Yast - class SuSEFirewallExpertRulesClient < Client - def main - Yast.include self, "testsuite.rb" - # testedfiles: SuSEFirewallExpertRules - - Yast.import "SuSEFirewallExpertRules" - - DUMP("== IsValidNetwork ==") - - @valid_network_definitions = [ - "192.168.0.1", - "192.168.0.0/24", - "192.168.0.1/32", - "192.168.0.0/255.255.0.0", - "192.168.0.0/255.255.224.0", - "0/0", - "192.168.0.1/16", - "192.168.0.1/0" - ] - - @invalid_network_definitions = [ - "192.168.0.355", - "192.168.0.0/255.255.333.0", - "192.168.0.1/888", - "192.168.0.1/33" - ] - - DUMP("All these should be *valid* (true):") - Builtins.foreach(@valid_network_definitions) do |check_this| - TEST(->() { SuSEFirewallExpertRules.IsValidNetwork(check_this) }, [], nil) - end - - DUMP("All these should be *invalid* (false):") - Builtins.foreach(@invalid_network_definitions) do |check_this| - TEST(->() { SuSEFirewallExpertRules.IsValidNetwork(check_this) }, [], nil) - end - - DUMP("Testing adding/reading expert rules") - # Rules are empty at the beginning - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - TEST(lambda do - SuSEFirewallExpertRules.AddNewAcceptRule( - "EXT", - - "network" => "192.168.0.1/255.255.240.0", - "protocol" => "tcp", - "sport" => "22", - "options" => "hitcount=3,blockseconds=60,recentname=ssh" - - ) - end, [], nil) - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - TEST(lambda do - SuSEFirewallExpertRules.AddNewAcceptRule( - "EXT", - - "network" => "192.168.0.1/255.255.240.0", - "protocol" => "tcp", - "options" => "whatever=1" - - ) - end, [], nil) - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - # Deleting by rule ID (offset in list) - TEST(->() { SuSEFirewallExpertRules.DeleteRuleID("EXT", 0) }, [], nil) - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - DUMP("Cannot remove rule that doesn't exist") - TEST(lambda do - SuSEFirewallExpertRules.RemoveAcceptRule( - "EXT", - "network" => "192.168.0.1/255.255.240.0", "protocol" => "tcp" - ) - end, [], nil) - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - # Now "options" match too - TEST(lambda do - SuSEFirewallExpertRules.RemoveAcceptRule( - "EXT", - - "network" => "192.168.0.1/255.255.240.0", - "protocol" => "tcp", - "options" => "whatever=1" - - ) - end, [], nil) - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - DUMP("Adding special rule allowed 'from all networks'") - TEST(lambda do - SuSEFirewallExpertRules.AddNewAcceptRule( - "EXT", - "protocol" => "UDP", "sport" => "888" - ) - end, [], nil) - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - # Special all-IPv4-networks-(only) rule - TEST(lambda do - SuSEFirewallExpertRules.AddNewAcceptRule( - "EXT", - "protocol" => "TCP", "sport" => "999", "network" => "0.0.0.0/0" - ) - end, [], nil) - TEST(->() { SuSEFirewallExpertRules.GetListOfAcceptRules("EXT") }, [], nil) - - DUMP("== Done ==") - - nil - end - end -end - -Yast::SuSEFirewallExpertRulesClient.new.main diff --git a/package/yast2.changes b/package/yast2.changes index 6a6752319..918a92574 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,15 @@ +------------------------------------------------------------------- +Mon Sep 10 13:02:04 UTC 2018 - knut.anderssen@suse.com + +- Extended the firewall API supporting the use of single-value + attributes and also prepared it for introducing more complex + relations like 'forward-ports' and 'rich-rules' in the future. + (fate#324662) +- Improved the parser for zones and added a parser for services. +- Improved test mocking fixing a Polkit popup shown when running the + test (bsc#087867) +- 4.0.88 + ------------------------------------------------------------------- Wed Sep 5 10:19:27 UTC 2018 - jlopez@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 67498e2e3..0b44ad877 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.87 +Version: 4.0.88 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From d783dab4f0c70fa03f24abcf2c5f90cf71553240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 12 Sep 2018 14:00:18 +0100 Subject: [PATCH 108/223] Bring back the removed SuSEFirewallProposal --- .../network/src/lib/network/susefirewalld.rb | 7 + .../src/modules/SuSEFirewallProposal.rb | 785 ++++++++++++++++++ .../test/susefirewall_proposal_test.rb | 109 +++ 3 files changed, 901 insertions(+) create mode 100644 library/network/src/modules/SuSEFirewallProposal.rb create mode 100755 library/network/test/susefirewall_proposal_test.rb diff --git a/library/network/src/lib/network/susefirewalld.rb b/library/network/src/lib/network/susefirewalld.rb index 568f9d010..dbef10f53 100644 --- a/library/network/src/lib/network/susefirewalld.rb +++ b/library/network/src/lib/network/susefirewalld.rb @@ -1017,6 +1017,12 @@ def SetAcceptExpertRules(zone, expert_rules) true end + # FIXME: this method currently does nothing at all and has been added just + # for having the same API than SuSEFirewall2 but it is deprecated + def full_init_on_boot(new_state) + new_state + end + private def set_zone_modified(zone, zone_params) @@ -1338,5 +1344,6 @@ def set_ports_with_protocol_to_zone(ports, protocol, zone) publish function: :AddService, type: "boolean (string, string, string)" publish function: :RemoveService, type: "boolean (string, string, string)" publish function: :AddXenSupport, type: "void ()" + publish function: :full_init_on_boot, type: "boolean (boolean)" end end diff --git a/library/network/src/modules/SuSEFirewallProposal.rb b/library/network/src/modules/SuSEFirewallProposal.rb new file mode 100644 index 000000000..0ef8aa33b --- /dev/null +++ b/library/network/src/modules/SuSEFirewallProposal.rb @@ -0,0 +1,785 @@ +# encoding: utf-8 + +# *************************************************************************** +# +# Copyright (c) 2002 - 2012 Novell, Inc. +# 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 Novell, Inc. +# +# To contact Novell about this file by physical or electronic mail, +# you may find current contact information at www.novell.com +# +# *************************************************************************** +# File: modules/SuSEFirewallProposal.ycp +# Package: SuSEFirewall configuration +# Summary: Functional interface for SuSEFirewall installation proposal +# Authors: Lukas Ocilka +# +# $Id$ +# +# This module provides a functional API for Installation proposal of SuSEfirewall2 +require "yast" + +module Yast + class SuSEFirewallProposalClass < Module + include Yast::Logger + + def main + textdomain "base" + + Yast.import "SuSEFirewall" + Yast.import "ProductFeatures" + Yast.import "Linuxrc" + Yast.import "Package" + Yast.import "SuSEFirewallServices" + + # + + # proposal was changed by user + @proposal_changed_by_user = false + + # proposal was initialized yet + @proposal_initialized = false + + # known interfaces + @known_interfaces = [] + + # warnings for this "turn" + @warnings_now = [] + + @vnc_fallback_ports = ["5801", "5901"] + + # bnc #427708, yet another name of service + @vnc_service = "service:xorg-x11-server" + + @ssh_service = "service:sshd" + end + + # + + # + + # Local function adds another warning string into warnings for user + # + # @param [String] warning + def AddWarning(warning) + @warnings_now = Builtins.add(@warnings_now, warning) + + nil + end + + # Local function clears all warnings for user from memory + def ClearWarnings + @warnings_now = [] + + nil + end + + # Function returns list of warnings for user + # + # @return [Array] of warnings + def GetWarnings + deep_copy(@warnings_now) + end + + # Local function sets currently known interfaces. + # + # @param [Array] interfaces list of known interfaces + def SetKnownInterfaces(interfaces) + interfaces = deep_copy(interfaces) + @known_interfaces = deep_copy(interfaces) + + nil + end + + # Local function returns list [string] of known interfaces. + # They must have been set using SetKnownInterfaces(list [string] interfaces) + # function. + # + # @return [Array] of known interfaces + def GetKnownInterfaces + deep_copy(@known_interfaces) + end + + # Function returns if interface is a dial-up type. + # + # @return [Boolean] if is dial-up interface + def IsDialUpInterface(interface) + all_interfaces = SuSEFirewall.GetAllKnownInterfaces + + interface_type = nil + Builtins.foreach(all_interfaces) do |one| + next if Ops.get(one, "id") != interface + # this is THE interface + interface_type = Ops.get(one, "type") + end + + interface_type == "dialup" + end + + # Local function adds list of interfaces into zone. + # + # @param [Array] interfaces + # @param [String] zone + def SetInterfacesToZone(interfaces, zone) + interfaces = deep_copy(interfaces) + Builtins.foreach(interfaces) do |interface| + SuSEFirewall.AddInterfaceIntoZone(interface, zone) + end + + nil + end + + # Local function for updating user-changed proposal. + def UpdateProposal + last_known_interfaces = GetKnownInterfaces() + currently_known_interfaces = SuSEFirewall.GetListOfKnownInterfaces + + had_dialup_interfaces = false + Builtins.foreach(last_known_interfaces) do |this_interface| + if IsDialUpInterface(this_interface) + had_dialup_interfaces = true + raise Break + end + end + + Builtins.foreach(currently_known_interfaces) do |interface| + # already known but not assigned + next if Builtins.contains(last_known_interfaces, interface) + # already configured in some zone + next if !SuSEFirewall.GetZoneOfInterface(interface).nil? + # any dial-up interfaces presented and the new one isn't dial-up + if had_dialup_interfaces && !IsDialUpInterface(interface) + AddWarning( + Builtins.sformat( + # TRANSLATORS: Warning in installation proposal, %1 is a device name (eth0, sl0, ...) + _( + "New network device '%1' found; added as an internal firewall interface" + ), + interface + ) + ) + SetInterfacesToZone([interface], "INT") + else + AddWarning( + Builtins.sformat( + # TRANSLATORS: Warning in installation proposal, %1 is a device name (eth0, sl0, ...) + _( + "New network device '%1' found; added as an external firewall interface" + ), + interface + ) + ) + SetInterfacesToZone([interface], "EXT") + end + end + + SetKnownInterfaces(currently_known_interfaces) + + nil + end + + # Returns whether service is enabled in zones. + # + # @param [String] service + # @param [Array] zones + # @return [Boolean] if enabled + def ServiceEnabled(service, zones) + zones = deep_copy(zones) + if service.nil? || service == "" + Builtins.y2error("Ups, service: %1?", service) + return false + end + + if zones.nil? || zones == [] + Builtins.y2error("Ups, zones: %1?", zones) + return false + end + + serenabled = true + + serstat = SuSEFirewall.GetServices([service]) + Builtins.foreach(zones) do |one_zone| + if Ops.get(serstat, [service, one_zone]) == false + Builtins.y2milestone( + "Service %1 is not enabled in %2", + service, + one_zone + ) + serenabled = false + raise Break + end + end + + serenabled + end + + # Enables ports in zones. + # + # @param [Array] fallback_ports fallback TCP ports + # @param [Array] zones + def EnableFallbackPorts(fallback_ports, zones) + known_zones = SuSEFirewall.GetKnownFirewallZones() + unknown_zones = zones - known_zones + raise "Unknown firewall zones #{unknown_zones}" unless unknown_zones.empty? + + log.info "Enabling fallback ports: #{fallback_ports} in zones: #{zones}" + zones.each do |one_zone| + fallback_ports.each do |one_port| + SuSEFirewall.AddService(one_port, "TCP", one_zone) + end + end + + nil + end + + # Function opens service for network interfaces given as the third parameter. + # Fallback ports are used if the given service is uknown. + # If interfaces are not assigned to any firewall zone, all zones will be used. + # + # @see OpenServiceOnNonDialUpInterfaces for more info. + # + # @param [String] service e.g., "service:http-server" + # @param [Array] fallback_ports e.g., ["80"] + # @param [Array] interfaces e.g., ["eth3"] + def OpenServiceInInterfaces(service, fallback_ports, interfaces) + fallback_ports = deep_copy(fallback_ports) + interfaces = deep_copy(interfaces) + zones = SuSEFirewall.GetZonesOfInterfaces(interfaces) + + # Interfaces might not be assigned to any zone yet, use all zones + zones = SuSEFirewall.GetKnownFirewallZones() if zones.empty? + + if SuSEFirewallServices.IsKnownService(service) + log.info "Opening service #{service} on interfaces #{interfaces} (zones #{zones})" + SuSEFirewall.SetServicesForZones([service], zones, true) + else + log.warn "Unknown service #{service}, enabling fallback ports" + EnableFallbackPorts(fallback_ports, zones) + end + + nil + end + + # Checks whether the given service or (TCP) ports are open at least in + # one FW zone. + # + # @param [String] service e.g., "service:http-server" + # @param [Array] fallback_ports e.g., ["80"] + def IsServiceOrPortsOpen(service, fallback_ports) + fallback_ports = deep_copy(fallback_ports) + ret = false + + Builtins.foreach(SuSEFirewall.GetKnownFirewallZones) do |zone| + # either service is supported + if SuSEFirewall.IsServiceSupportedInZone(service, zone) + ret = true + # or check for ports + else + all_ports = true + + # all ports have to be open + Builtins.foreach(fallback_ports) do |port| + if !SuSEFirewall.HaveService(port, "TCP", zone) + all_ports = false + raise Break + end + end + + ret = true if all_ports + end + raise Break if ret == true + end + + ret + end + + # Function opens up the service on all non-dial-up network interfaces. + # If there are no network interfaces known and the 'any' feature is supported, + # function opens the service for the zone supporting that feature. If there + # are only dial-up interfaces, function opens the service for them. + # + # @param [String] service such as "service:koo" or "serice:boo" + # @param [Array] fallback_ports list of ports used as a fallback if the given service doesn't exist + def OpenServiceOnNonDialUpInterfaces(service, fallback_ports) + fallback_ports = deep_copy(fallback_ports) + non_dial_up_interfaces = SuSEFirewall.GetAllNonDialUpInterfaces + dial_up_interfaces = SuSEFirewall.GetAllDialUpInterfaces + + # Opening the service for non-dial-up interfaces + if Ops.greater_than(Builtins.size(non_dial_up_interfaces), 0) + OpenServiceInInterfaces(service, fallback_ports, non_dial_up_interfaces) + # Only dial-up network interfaces, there mustn't be any non-dial-up one + elsif Ops.greater_than(Builtins.size(dial_up_interfaces), 0) + OpenServiceInInterfaces(service, fallback_ports, dial_up_interfaces) + # No network interfaces are known + elsif Builtins.size(@known_interfaces) == 0 + if SuSEFirewall.IsAnyNetworkInterfaceSupported == true + Builtins.y2warning( + "WARNING: Opening %1 for the External zone without any known interface!", + Builtins.toupper(service) + ) + OpenServiceInInterfaces( + service, + fallback_ports, + [SuSEFirewall.special_all_interface_string] + ) + end + end + + nil + end + + # Local function returns whether the Xen kernel is installed + # + # @return [Boolean] whether xen-capable kernel is installed. + def IsXenInstalled + # bug #154133 + return true if Package.Installed("kernel-xen") + return true if Package.Installed("kernel-xenpae") + + false + end + + # Local function for proposing firewall configuration. + def ProposeFunctions + known_interfaces = SuSEFirewall.GetAllKnownInterfaces + + dial_up_interfaces = [] + non_dup_interfaces = [] + Builtins.foreach(known_interfaces) do |interface| + if Ops.get(interface, "type") == "dial_up" + dial_up_interfaces = Builtins.add( + dial_up_interfaces, + Ops.get(interface, "id", "") + ) + else + non_dup_interfaces = Builtins.add( + non_dup_interfaces, + Ops.get(interface, "id", "") + ) + end + end + + Builtins.y2milestone( + "Proposal based on configuration: Dial-up interfaces: %1, Other: %2", + dial_up_interfaces, + non_dup_interfaces + ) + + # has any network interface + if Builtins.size(non_dup_interfaces) == 0 || + Builtins.size(dial_up_interfaces) == 0 + SuSEFirewall.SetEnableService( + ProductFeatures.GetBooleanFeature("globals", "enable_firewall") + ) + SuSEFirewall.SetStartService( + ProductFeatures.GetBooleanFeature("globals", "enable_firewall") + ) + end + + # has non-dial-up and also dial-up interfaces + if Ops.greater_than(Builtins.size(non_dup_interfaces), 0) && + Ops.greater_than(Builtins.size(dial_up_interfaces), 0) + SetInterfacesToZone(non_dup_interfaces, "INT") + SetInterfacesToZone(dial_up_interfaces, "EXT") + if ProductFeatures.GetBooleanFeature("globals", "firewall_enable_ssh") + SuSEFirewall.SetServicesForZones([@ssh_service], ["INT", "EXT"], true) + end + + # has non-dial-up and doesn't have dial-up interfaces + elsif Ops.greater_than(Builtins.size(non_dup_interfaces), 0) && + Builtins.size(dial_up_interfaces) == 0 + SetInterfacesToZone(non_dup_interfaces, "EXT") + if ProductFeatures.GetBooleanFeature("globals", "firewall_enable_ssh") + SuSEFirewall.SetServicesForZones([@ssh_service], ["EXT"], true) + end + + # doesn't have non-dial-up and has dial-up interfaces + elsif Builtins.size(non_dup_interfaces) == 0 && + Ops.greater_than(Builtins.size(dial_up_interfaces), 0) + SetInterfacesToZone(dial_up_interfaces, "EXT") + if ProductFeatures.GetBooleanFeature("globals", "firewall_enable_ssh") + SuSEFirewall.SetServicesForZones([@ssh_service], ["EXT"], true) + end + end + + # Dial-up interfaces are considered to be internal, + # Non-dial-up are considered to be external. + # If there are only Non-dial-up interfaces, they are all considered as external. + # + # VNC Installation proposes to open VNC Access up on the Non-dial-up interfaces only. + # SSH Installation is the same case... + if Linuxrc.vnc + Builtins.y2milestone( + "This is an installation over VNC, opening VNC on all non-dial-up interfaces..." + ) + # Try the service first, then ports + # bnc #398855 + OpenServiceOnNonDialUpInterfaces(@vnc_service, @vnc_fallback_ports) + end + if Linuxrc.usessh + Builtins.y2milestone( + "This is an installation over SSH, opening SSH on all non-dial-up interfaces..." + ) + # Try the service first, then ports + # bnc #398855 + OpenServiceOnNonDialUpInterfaces(@ssh_service, ["ssh"]) + end + + # Firewall support for XEN domain0 + if IsXenInstalled() + Builtins.y2milestone( + "Adding Xen support into the firewall configuration" + ) + SuSEFirewall.AddXenSupport + end + + propose_iscsi if Linuxrc.useiscsi + + SetKnownInterfaces(SuSEFirewall.GetListOfKnownInterfaces) + + nil + end + + # + + # + + # Function sets that proposal was changed by user + # + # @param changed [true, false] if changed by user + def SetChangedByUser(changed) + Builtins.y2milestone("Proposal was changed by user") + @proposal_changed_by_user = changed + + nil + end + + # Local function returns if proposal was changed by user + # + # @return [Boolean] if proposal was changed by user + def GetChangedByUser + @proposal_changed_by_user + end + + # Function sets that proposal was initialized + # + # @param initialized [true, false] if initialized + def SetProposalInitialized(initialized) + @proposal_initialized = initialized + + nil + end + + # Local function returns if proposal was initialized already + # + # @return [Boolean] if proposal was initialized + def GetProposalInitialized + @proposal_initialized + end + + # Function fills up default configuration into internal values + # + # @return [void] + def Reset + SuSEFirewall.ResetReadFlag + SuSEFirewall.Read + + nil + end + + # Function proposes the SuSEfirewall2 configuration + # + # @return [void] + def Propose + # No proposal when SuSEfirewall2 is not installed + if !SuSEFirewall.SuSEFirewallIsInstalled + SuSEFirewall.SetEnableService(false) + SuSEFirewall.SetStartService(false) + return nil + end + + # Not changed by user - Propose from scratch + if !GetChangedByUser() + Builtins.y2milestone("Calling firewall configuration proposal") + Reset() + ProposeFunctions() + # Changed - don't break user's configuration + else + Builtins.y2milestone("Calling firewall configuration update proposal") + UpdateProposal() + end + + nil + end + + # Function returns the proposal summary + # + # @return [Hash{String => String}] proposal + # + # **Structure:** + # + # map $[ + # "output" : "HTML Proposal Summary", + # "warning" : "HTML Warning Summary", + # ] + def ProposalSummary + # output: $[ "output" : "HTML Proposal", "warning" : "HTML Warning" ]; + output = "" + warning = "" + + # SuSEfirewall2 package needn't be installed + if !SuSEFirewall.SuSEFirewallIsInstalled + # TRANSLATORS: Proposal informative text + output = "
    " + + _( + "SuSEfirewall2 package is not installed, firewall will be disabled." + ) + "
" + + return { "output" => output, "warning" => warning } + end + + # SuSEfirewall2 is installed... + + firewall_is_enabled = SuSEFirewall.GetEnableService == true + + output = Ops.add(output, "
    \n") + output = Ops.add( + Ops.add( + Ops.add(output, "
  • "), + if firewall_is_enabled + # TRANSLATORS: Proposal informative text "Firewall is enabled (disable)" with link around + # IMPORTANT: Please, do not change the HTML link ..., only visible text + _( + "Firewall is enabled (disable)" + ) + else + # TRANSLATORS: Proposal informative text "Firewall is disabled (enable)" with link around + # IMPORTANT: Please, do not change the HTML link ..., only visible text + _( + "Firewall is disabled (enable)" + ) + end + ), + "
  • \n" + ) + + if firewall_is_enabled + # Any enabled SSH means SSH-is-enabled + is_ssh_enabled = false + + # Any known interfaces + if Ops.greater_than(Builtins.size(@known_interfaces), 0) + Builtins.y2milestone("Interfaces: %1", @known_interfaces) + + # all known interfaces for testing + used_zones = SuSEFirewall.GetZonesOfInterfacesWithAnyFeatureSupported( + @known_interfaces + ) + Builtins.y2milestone("Zones used by firewall: %1", used_zones) + + Builtins.foreach(used_zones) do |zone| + if SuSEFirewall.IsServiceSupportedInZone(@ssh_service, zone) || + SuSEFirewall.HaveService("ssh", "TCP", zone) + is_ssh_enabled = true + end + end + + output = Ops.add( + Ops.add( + Ops.add(output, "
  • "), + if is_ssh_enabled + # TRANSLATORS: Network proposal informative text with link around + # IMPORTANT: Please, do not change the HTML link ..., only visible text + _( + "SSH port is open (close)" + ) + else + # TRANSLATORS: Network proposal informative text with link around + # IMPORTANT: Please, do not change the HTML link ..., only visible text + _( + "SSH port is blocked (open)" + ) + end + ), + "
  • \n" + ) + + # No known interfaces, but 'any' is supported + # and ssh is enabled there + elsif SuSEFirewall.IsAnyNetworkInterfaceSupported && + SuSEFirewall.IsServiceSupportedInZone( + @ssh_service, + SuSEFirewall.special_all_interface_zone + ) + is_ssh_enabled = true + # TRANSLATORS: Network proposal informative text with link around + # IMPORTANT: Please, do not change the HTML link ..., only visible text + output = Ops.add( + Ops.add( + Ops.add(output, "
  • "), + _( + "SSH port is open (close), but\nthere are no network interfaces configured" + ) + ), + "
  • " + ) + end + Builtins.y2milestone( + "SSH is " + (is_ssh_enabled ? "" : "not ") + "enabled" + ) + + if Linuxrc.usessh + if !is_ssh_enabled + # TRANSLATORS: This is a warning message. Installation over SSH without SSH allowed on firewall + AddWarning( + _( + "You are installing a system over SSH, but you have not opened the SSH port on the firewall." + ) + ) + end + end + + # when the firewall is enabled and we are installing the system over VNC + if Linuxrc.vnc + # Any enabled VNC means VNC-is-enabled + is_vnc_enabled = false + if Ops.greater_than(Builtins.size(@known_interfaces), 0) + Builtins.foreach( + SuSEFirewall.GetZonesOfInterfacesWithAnyFeatureSupported( + @known_interfaces + ) + ) do |zone| + if SuSEFirewall.IsServiceSupportedInZone(@vnc_service, zone) == true + is_vnc_enabled = true + # checking also fallback ports + else + set_vnc_enabled_to = true + Builtins.foreach(@vnc_fallback_ports) do |one_port| + if SuSEFirewall.HaveService(one_port, "TCP", zone) != true + set_vnc_enabled_to = false + raise Break + end + is_vnc_enabled = true if set_vnc_enabled_to == true + end + end + end + end + Builtins.y2milestone( + "VNC port is " + (is_vnc_enabled ? "open" : "blocked") + " in the firewall" + ) + + output = Ops.add( + Ops.add( + Ops.add(output, "
  • "), + if is_vnc_enabled + # TRANSLATORS: Network proposal informative text "Remote Administration (VNC) is enabled" with link around + # IMPORTANT: Please, do not change the HTML link ..., only visible text + _( + "Remote Administration (VNC) ports are open (close)" + ) + else + # TRANSLATORS: Network proposal informative text "Remote Administration (VNC) is disabled" with link around + # IMPORTANT: Please, do not change the HTML link ..., only visible text + _( + "Remote Administration (VNC) ports are blocked (open)" + ) + end + ), + "
  • \n" + ) + + if !is_vnc_enabled + # TRANSLATORS: This is a warning message. Installation over VNC without VNC allowed on firewall + AddWarning( + _( + "You are installing a system using remote administration (VNC), but you have not opened the VNC ports on the firewall." + ) + ) + end + end + + if Linuxrc.useiscsi + is_iscsi_enabled = IsServiceOrPortsOpen( + @iscsi_target_service, + @iscsi_target_fallback_ports + ) + + output = Ops.add( + Ops.add( + Ops.add(output, "
  • "), + if is_iscsi_enabled + # TRANSLATORS: Network proposal informative text + _("iSCSI Target ports are open") + else + # TRANSLATORS: Network proposal informative text + _("iSCSI Target ports are blocked") + end + ), + "
  • \n" + ) + + if !is_iscsi_enabled + # TRANSLATORS: This is a warning message. Installation to iSCSI without iSCSI allowed on firewall + AddWarning( + _( + "You are installing a system using iSCSI Target, but you have not opened the needed ports on the firewall." + ) + ) + end + end + + warnings_strings = GetWarnings() + if Ops.greater_than(Builtins.size(warnings_strings), 0) + ClearWarnings() + Builtins.foreach(warnings_strings) do |single_warning| + warning = Ops.add( + Ops.add(Ops.add(warning, "
  • "), single_warning), + "
  • \n" + ) + end + warning = Ops.add(Ops.add("
      \n", warning), "
    \n") + end + end + + output = Ops.add(output, "
\n") + + { "output" => output, "warning" => warning } + end + + # Proposes firewall settings for iSCSI + def propose_iscsi + log.info "iSCSI has been used during installation, proposing FW full_init_on_boot" + + # bsc#916376: ports need to be open already during boot + SuSEFirewall.full_init_on_boot(true) + + nil + end + + publish function: :OpenServiceOnNonDialUpInterfaces, type: "void (string, list )" + publish function: :SetChangedByUser, type: "void (boolean)" + publish function: :GetChangedByUser, type: "boolean ()" + publish function: :SetProposalInitialized, type: "void (boolean)" + publish function: :GetProposalInitialized, type: "boolean ()" + publish function: :Reset, type: "void ()" + publish function: :Propose, type: "void ()" + publish function: :ProposalSummary, type: "map ()" + publish function: :propose_iscsi, type: "void ()" + end + + SuSEFirewallProposal = SuSEFirewallProposalClass.new + SuSEFirewallProposal.main +end diff --git a/library/network/test/susefirewall_proposal_test.rb b/library/network/test/susefirewall_proposal_test.rb new file mode 100755 index 000000000..520f3385a --- /dev/null +++ b/library/network/test/susefirewall_proposal_test.rb @@ -0,0 +1,109 @@ +#!/usr/bin/env rspec + +require_relative "test_helper" + +Yast.import "SuSEFirewall" +Yast.import "SuSEFirewallServices" +Yast.import "SuSEFirewallProposal" +Yast.import "Linuxrc" + +describe Yast::SuSEFirewallProposal do + subject { Yast::SuSEFirewallProposal } + + describe "#ProposeFunctions" do + context "when iscsi is used" do + it "calls the iscsi proposal" do + allow(Yast::Linuxrc).to receive(:useiscsi).and_return(true) + expect(subject).to receive(:propose_iscsi).and_return(nil) + + subject.ProposeFunctions + end + end + + context "when iscsi is not used" do + it "does not call the iscsi proposal" do + allow(Yast::Linuxrc).to receive(:useiscsi).and_return(false) + expect(subject).not_to receive(:propose_iscsi) + + subject.ProposeFunctions + end + end + end + + describe "#propose_iscsi" do + it "proposes full firewall initialization on boot" do + expect(Yast::SuSEFirewall).to receive(:full_init_on_boot).and_return(true) + + subject.propose_iscsi + end + end + + describe "#EnableFallbackPorts" do + let(:fallback_ports) { ["port1", "port2"] } + + before(:each) do + allow(Yast::SuSEFirewall).to receive(:GetKnownFirewallZones).and_return(["EXT", "INT", "DMZ"]) + end + + context "when opening ports in known firewall zones" do + it "opens given ports in firewall in given zones" do + expect(Yast::SuSEFirewall).to receive(:AddService).with(/port.*/, "TCP", /(EXT|DMZ)/).exactly(4).times + + subject.EnableFallbackPorts(fallback_ports, ["EXT", "DMZ"]) + end + end + + context "when opening ports in unknown firewall zones" do + it "throws an exception" do + method_call = proc { subject.EnableFallbackPorts(fallback_ports, ["UNKNOWN_ZONE1", "UZ2"]) } + expect { method_call.call }.to raise_error(/UNKNOWN_ZONE1.*UZ2/) + end + end + end + + describe "#OpenServiceInInterfaces" do + let(:network_interfaces) { ["eth-x", "eth-y"] } + let(:interfaces_zones) { ["ZONE1", "ZONE2"] } + let(:all_zones) { ["ZONE1", "ZONE2", "ZONE3"] } + let(:firewall_service) { "service:fw_service_x" } + let(:fallback_ports) { ["p1", "p2", "p3"] } + + before(:each) do + # Default behavior: Interfaces are assigned to zones, there are more known zones, + # given firewall service exists + allow(Yast::SuSEFirewall).to receive(:GetZonesOfInterfaces).and_return(interfaces_zones) + allow(Yast::SuSEFirewall).to receive(:GetKnownFirewallZones).and_return(all_zones) + allow(Yast::SuSEFirewallServices).to receive(:IsKnownService).and_return(true) + end + + context "when network interfaces are assigned to some zone(s)" do + it "open service in firewall in zones that include given interfaces" do + expect(Yast::SuSEFirewall).to receive(:SetServicesForZones).with([firewall_service], interfaces_zones, true) + subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) + end + end + + context "when network interfaces are not assigned to any zone" do + it "opens service in firewall in all zones" do + allow(Yast::SuSEFirewall).to receive(:GetZonesOfInterfaces).and_return([]) + expect(Yast::SuSEFirewall).to receive(:SetServicesForZones).with([firewall_service], all_zones, true) + subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) + end + end + + context "when given firewall service is known" do + it "opens service in firewall in zones that include given interfaces" do + expect(Yast::SuSEFirewall).to receive(:SetServicesForZones).with([firewall_service], interfaces_zones, true) + subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) + end + end + + context "when given service is unknown" do + it "opens given fallback ports in zones that include given interfaces" do + allow(Yast::SuSEFirewallServices).to receive(:IsKnownService).and_return(false) + expect(subject).to receive(:EnableFallbackPorts).with(fallback_ports, interfaces_zones) + subject.OpenServiceInInterfaces(firewall_service, fallback_ports, network_interfaces) + end + end + end +end From dee0cb73902e4a107c515e87974ceec43cd753fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 12 Sep 2018 14:02:38 +0100 Subject: [PATCH 109/223] Bump version & changelog. --- package/yast2.changes | 10 +++++++++- package/yast2.spec | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package/yast2.changes b/package/yast2.changes index 918a92574..30bdc619a 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Wed Sep 12 13:00:43 UTC 2018 - knut.anderssen@suse.com + +- Bring back the SuSEFirewallProposal fixing the class unit tests + until yast2-network drops the import of the module completely. + (bsc#1087867) +- 4.0.89 + ------------------------------------------------------------------- Mon Sep 10 13:02:04 UTC 2018 - knut.anderssen@suse.com @@ -7,7 +15,7 @@ Mon Sep 10 13:02:04 UTC 2018 - knut.anderssen@suse.com (fate#324662) - Improved the parser for zones and added a parser for services. - Improved test mocking fixing a Polkit popup shown when running the - test (bsc#087867) + test (bsc#1087867) - 4.0.88 ------------------------------------------------------------------- diff --git a/package/yast2.spec b/package/yast2.spec index 0b44ad877..ced9272e6 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.88 +Version: 4.0.89 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 9d2b78ef97a93bf92f554f1cfaf889a5a9b4d8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 12 Sep 2018 15:03:17 +0100 Subject: [PATCH 110/223] Added forgeted file into Makefile.am --- library/network/src/Makefile.am | 1 + library/network/test/Makefile.am | 1 + 2 files changed, 2 insertions(+) diff --git a/library/network/src/Makefile.am b/library/network/src/Makefile.am index e85415163..64597e0b1 100644 --- a/library/network/src/Makefile.am +++ b/library/network/src/Makefile.am @@ -5,6 +5,7 @@ module_DATA = \ modules/SuSEFirewall.rb \ modules/NetworkConfig.rb \ modules/Internet.rb \ + modules/SuSEFirewallProposal.rb \ modules/PortRanges.rb \ modules/NetworkInterfaces.rb \ modules/CWMFirewallInterfaces.rb \ diff --git a/library/network/test/Makefile.am b/library/network/test/Makefile.am index d0b2fcd21..6f92c513e 100644 --- a/library/network/test/Makefile.am +++ b/library/network/test/Makefile.am @@ -3,6 +3,7 @@ TESTS = \ network_interfaces_test.rb \ network_interfaces_helpers_test.rb \ network_service_test.rb \ + susefirewall_proposal_test.rb \ susefirewall_services_test.rb \ susefirewall_test.rb \ susefirewalld_test.rb From 4aaac0d94826b28717dde59358245713694792da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 12 Sep 2018 15:07:10 +0100 Subject: [PATCH 111/223] Bump version & changelog. --- package/yast2.changes | 6 ++++++ package/yast2.spec | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 30bdc619a..5cf721208 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Sep 12 14:05:49 UTC 2018 - knut.anderssen@suse.com + +- Added the missing SuSEFirewallProposal.rb file to the Makefile +- 4.0.90 + ------------------------------------------------------------------- Wed Sep 12 13:00:43 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index ced9272e6..fada4acc2 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.89 +Version: 4.0.90 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 03a10a42e17d7ea1e1ee2c9770be8d4ceca6cd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 12 Sep 2018 13:10:00 +0100 Subject: [PATCH 112/223] Avoid to always return :next - When the dialog is accepted by using other button different to :next (e.g., :ok) it returns the proper button id. --- library/cwm/src/modules/CWM.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index b5181f9c8..21ac4597f 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -834,7 +834,6 @@ def Run(widgets, functions, skip_store_for: []) end end - ret = :next if ret == :ok ret = :abort if ret == :cancel if ret == :next && functions[:next] From 297d8f2607e9415916083a5a74910334211aeaaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 12 Sep 2018 13:16:18 +0100 Subject: [PATCH 113/223] Update version and changelog --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 5cf721208..de5d5b6da 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Sep 12 14:11:56 UTC 2018 - jlopez@suse.com + +- CWM: avoid to always return :next when accepting a dialog. +- Needed for Expert Partitioner (fate#318196). +- 4.0.91 + ------------------------------------------------------------------- Wed Sep 12 14:05:49 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index fada4acc2..cfd7ba9bf 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.90 +Version: 4.0.91 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 5f61b95b9b984d9e7e904915a3c4db8e133a49b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Mon, 17 Sep 2018 12:21:19 +0100 Subject: [PATCH 114/223] Use remove-service-from-zone when firewalld is not running --- .../src/lib/y2firewall/firewalld/api/zones.rb | 6 ++--- .../y2firewall/firewalld/api/zones_test.rb | 23 +++++++++++++++++++ .../test/y2firewall/firewalld/api_test.rb | 16 +++++++------ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/library/network/src/lib/y2firewall/firewalld/api/zones.rb b/library/network/src/lib/y2firewall/firewalld/api/zones.rb index 64c8c5028..66ad46b85 100644 --- a/library/network/src/lib/y2firewall/firewalld/api/zones.rb +++ b/library/network/src/lib/y2firewall/firewalld/api/zones.rb @@ -131,8 +131,7 @@ def add_interface(zone, interface, permanent: permanent?) # modifies the permanent configuration # @return [Boolean] True if the interface was removed from the zone def remove_interface(zone, interface, permanent: permanent?) - modify_command("--zone=#{zone}", "--remove-interface=#{interface}", - permanent: permanent) + modify_command("--zone=#{zone}", "--remove-interface=#{interface}", permanent: permanent) end # @param zone [String] The firewall zone @@ -234,7 +233,8 @@ def add_protocol(zone, protocol, permanent: permanent?) # modifies the permanent configuration # @return [Boolean] True if service was removed from zone def remove_service(zone, service, permanent: permanent?) - modify_command("--zone=#{zone}", "--remove-service=#{service}", permanent: permanent) + remove_arg = offline? ? "--remove-service-from-zone" : "--remove-service" + modify_command("--zone=#{zone}", "#{remove_arg}=#{service}", permanent: permanent) end # @param zone [String] The firewall zone diff --git a/library/network/test/y2firewall/firewalld/api/zones_test.rb b/library/network/test/y2firewall/firewalld/api/zones_test.rb index 83ed8f725..636a88e57 100755 --- a/library/network/test/y2firewall/firewalld/api/zones_test.rb +++ b/library/network/test/y2firewall/firewalld/api/zones_test.rb @@ -157,6 +157,29 @@ end end + describe "#add_service" do + it "adds the given service to the specified zone" do + expect(api).to receive(:modify_command) + .with("--zone=test", "--add-service=samba", permanent: api.permanent?) + + api.add_service("test", "samba") + end + end + + describe "#remove_service" do + let(:api_running) { Y2Firewall::Firewalld::Api.new(mode: :running) } + + it "removes the given service from the specified zone" do + expect(api).to receive(:modify_command) + .with("--zone=public", "--remove-service-from-zone=ssh", permanent: api.permanent?) + api.remove_service("public", "ssh") + + expect(api_running).to receive(:modify_command) + .with("--zone=public", "--remove-service=vnc", permanent: api_running.permanent?) + api_running.remove_service("public", "vnc") + end + end + describe "#source_enabled?" do it "returns false if the source is not binded to the zone" do allow(api).to receive(:query_command) diff --git a/library/network/test/y2firewall/firewalld/api_test.rb b/library/network/test/y2firewall/firewalld/api_test.rb index 3aa7dd181..41b11827d 100755 --- a/library/network/test/y2firewall/firewalld/api_test.rb +++ b/library/network/test/y2firewall/firewalld/api_test.rb @@ -149,6 +149,15 @@ end describe "#state" do + subject(:api) { described_class.new(mode: :running) } + + it "returns 'unknown' in case of an unexpected state" do + allow(Yast::Execute).to receive(:on_target) + .with("firewall-cmd", "--state", allowed_exitstatus: [0, 252]).and_return(24) + + expect(api.state).to eql("unknown") + end + it "returns 'running' when the firewall is running" do allow(Yast::Execute).to receive(:on_target) .with("firewall-cmd", "--state", allowed_exitstatus: [0, 252]).and_return(0) @@ -162,13 +171,6 @@ expect(api.state).to eql("not running") end - - it "returns 'unknown' in case of an unexpected state" do - allow(Yast::Execute).to receive(:on_target) - .with("firewall-cmd", "--state", allowed_exitstatus: [0, 252]).and_return(24) - - expect(api.state).to eql("unknown") - end end describe "#log_denied_packets" do From 244dc88ca55e74d24c5d5146518eb18aab8f96c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Mon, 17 Sep 2018 12:23:29 +0100 Subject: [PATCH 115/223] Bump version & changelog. --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index de5d5b6da..dfb07b39a 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Mon Sep 17 11:21:58 UTC 2018 - knut.anderssen@suse.com + +- Firewalld: Fixed the API cmd call for removing services from + zones when the firewall is in offline mode (bsc#1108628) +- 4.0.92 + ------------------------------------------------------------------- Wed Sep 12 14:11:56 UTC 2018 - jlopez@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index cfd7ba9bf..50ab60af8 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.91 +Version: 4.0.92 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 012851deda808b2eb6aecfdb8e30a9649ddccb2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Fri, 14 Sep 2018 10:16:17 +0100 Subject: [PATCH 116/223] Added firewalld system_service --- .../network/src/lib/y2firewall/firewalld.rb | 10 +++++++++ .../network/test/y2firewall/firewalld_test.rb | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/library/network/src/lib/y2firewall/firewalld.rb b/library/network/src/lib/y2firewall/firewalld.rb index 79c94b673..ee9b1c2d5 100644 --- a/library/network/src/lib/y2firewall/firewalld.rb +++ b/library/network/src/lib/y2firewall/firewalld.rb @@ -28,6 +28,7 @@ require "y2firewall/firewalld/zone" require "y2firewall/firewalld/zone_reader" require "y2firewall/firewalld/service_reader" +require "yast2/system_service" require "singleton" Yast.import "PackageSystem" @@ -224,10 +225,19 @@ def read? end # Convenience method to instantiate the firewalld API + # + # @return [Y2Firewall::Firewalld::Api] def api @api ||= Api.new end + # Convenience method to instantiate the firewalld system service + # + # @return [Yast2::SystemService, nil] + def system_service + @system_service ||= Yast2::SystemService.find(SERVICE) + end + private # Convenience method to instantiate a new zone reader diff --git a/library/network/test/y2firewall/firewalld_test.rb b/library/network/test/y2firewall/firewalld_test.rb index e723085f0..7a9a58472 100755 --- a/library/network/test/y2firewall/firewalld_test.rb +++ b/library/network/test/y2firewall/firewalld_test.rb @@ -196,6 +196,27 @@ end end + describe "#system_service" do + let(:service) { Yast2::SystemService.build(Y2Firewall::Firewalld::SERVICE) } + before do + allow(Yast2::SystemService).to receive(:find).and_return(service) + end + + context "if the firewalld service is found" do + it "returns the firewalld Yast2::SystemService object" do + expect(firewalld.system_service).to be_a Yast2::SystemService + end + end + + context "if the firewalld service is not found" do + let(:service) { nil } + + it "returns nil" do + expect(firewalld.system_service).to eq(nil) + end + end + end + describe "#read" do let(:zones_definition) do ["dmz", From 63bfd3a7393aefef019fe3578b42079c1912ce58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Fri, 14 Sep 2018 15:10:42 +0100 Subject: [PATCH 117/223] Bump version & changelog --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index dfb07b39a..cc9b134b6 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Mon Sep 17 11:52:51 UTC 2018 - knut.anderssen@suse.com + +- Y2Firewall::Firewalld: Added convenience method to obtain the + firewalld service object (fate#324662) +- 4.0.93 + ------------------------------------------------------------------- Mon Sep 17 11:21:58 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 50ab60af8..e7b451369 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.92 +Version: 4.0.93 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 7380fd1c25b6e1af5b12f5baadf2bcd5fe4785d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Thu, 6 Sep 2018 17:51:38 +0200 Subject: [PATCH 118/223] Find the latest version, don't use a hard-coded repo --- .../control/src/modules/WorkflowManager.rb | 19 ++++++++++++++----- .../packages/src/lib/y2packager/product.rb | 4 +++- .../src/lib/y2packager/product_reader.rb | 10 ++++++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/library/control/src/modules/WorkflowManager.rb b/library/control/src/modules/WorkflowManager.rb index f0ef49262..86d530777 100644 --- a/library/control/src/modules/WorkflowManager.rb +++ b/library/control/src/modules/WorkflowManager.rb @@ -1494,10 +1494,13 @@ def merge_product_workflow(product) log.info "Merging #{product.label} workflow" if merged_base_product - Yast::WorkflowManager.RemoveWorkflow(:package, 0, merged_base_product.installation_package) + Yast::WorkflowManager.RemoveWorkflow( + :package, + merged_base_product.installation_package_repo, + merged_base_product.installation_package) end - AddWorkflow(:package, 0, product.installation_package) + AddWorkflow(:package, product.installation_package_repo, product.installation_package) MergeWorkflows() RedrawWizardSteps() self.merged_base_product = product @@ -1604,12 +1607,18 @@ def package_repository(package_name) if pkgs.empty? log.warn("The installer extension package #{package_name} was not found") return nil - elsif pkgs.size > 1 + end + + latest_package = pkgs.reduce(nil) do |a, p| + !a || (Pkg.CompareVersions(a["version"], p["version"]) < 0) ? p : a + end + + if pkgs.size > 1 log.warn("More than one control package found: #{pkgs}") - log.warn("Using the first one: #{pkgs.first}") + log.info("Using the latest package: #{latest_package}") end - pkgs.first["source"] + latest_package["source"] end # Download and extract a package from a repository. diff --git a/library/packages/src/lib/y2packager/product.rb b/library/packages/src/lib/y2packager/product.rb index f572528ee..7d118cb22 100644 --- a/library/packages/src/lib/y2packager/product.rb +++ b/library/packages/src/lib/y2packager/product.rb @@ -41,13 +41,15 @@ class Product attr_reader :order # @return [String] package including installation.xml for install on top of lean os attr_reader :installation_package + # @return [Integer] repository for the installation_package + attr_reader :installation_package_repo class << self PKG_BINDINGS_ATTRS = ["name", "short_name", "display_name", "version", "arch", "category", "vendor"].freeze # Create a product from pkg-bindings hash data. - # @param p [Hash] the pkg-binindgs product hash + # @param p [Hash] the pkg-bindings product hash # @return [Y2Packager::Product] converted product def from_h(p) params = PKG_BINDINGS_ATTRS.each_with_object({}) { |a, h| h[a.to_sym] = p[a] } diff --git a/library/packages/src/lib/y2packager/product_reader.rb b/library/packages/src/lib/y2packager/product_reader.rb index 68be12a08..0d5654ba0 100644 --- a/library/packages/src/lib/y2packager/product_reader.rb +++ b/library/packages/src/lib/y2packager/product_reader.rb @@ -66,7 +66,7 @@ def installation_package_mapping # @return [Array] Available products def all_products @all_products ||= available_products.map do |prod| - prod_pkg = product_package(prod["product_package"], prod["source"]) + prod_pkg = product_package(prod["product_package"]) if prod_pkg prod_pkg["deps"].find { |dep| dep["provides"] =~ /\Adisplayorder\(\s*([0-9]+)\s*\)\z/ } @@ -114,10 +114,12 @@ def all_installed_products installed_products.map { |p| Y2Packager::Product.from_h(p) } end - def product_package(name, repo_id) + def product_package(name, _repo_id = nil) return nil unless name - Yast::Pkg.ResolvableDependencies(name, :package, "").find do |prod| - prod["source"] == repo_id + + # find the highest version + Yast::Pkg.ResolvableDependencies(name, :package, "").reduce(nil) do |a, p| + !a || (Yast::Pkg.CompareVersions(a["version"], p["version"]) < 0) ? p : a end end From 9fce2480e8c31f7998df567b915ccb81dc492b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Wed, 12 Sep 2018 13:45:36 +0200 Subject: [PATCH 119/223] Fixed tests --- library/control/src/modules/WorkflowManager.rb | 3 ++- library/control/test/workflow_manager_test.rb | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/library/control/src/modules/WorkflowManager.rb b/library/control/src/modules/WorkflowManager.rb index 86d530777..cbf4bca92 100644 --- a/library/control/src/modules/WorkflowManager.rb +++ b/library/control/src/modules/WorkflowManager.rb @@ -1497,7 +1497,8 @@ def merge_product_workflow(product) Yast::WorkflowManager.RemoveWorkflow( :package, merged_base_product.installation_package_repo, - merged_base_product.installation_package) + merged_base_product.installation_package + ) end AddWorkflow(:package, product.installation_package_repo, product.installation_package) diff --git a/library/control/test/workflow_manager_test.rb b/library/control/test/workflow_manager_test.rb index 0773ef455..d1efba15c 100755 --- a/library/control/test/workflow_manager_test.rb +++ b/library/control/test/workflow_manager_test.rb @@ -437,7 +437,8 @@ describe "#merge_product_workflow" do let(:product) do - instance_double("Y2Packager::Product", label: "SLES", installation_package: "package") + instance_double("Y2Packager::Product", label: "SLES", installation_package: "package", + installation_package_repo: 42) end before do @@ -448,7 +449,7 @@ end it "merges installation package workflow" do - expect(subject).to receive(:AddWorkflow).with(:package, 0, "package") + expect(subject).to receive(:AddWorkflow).with(:package, product.installation_package_repo, "package") subject.merge_product_workflow(product) end @@ -458,7 +459,7 @@ end it "removes the previous workflow" do - expect(subject).to receive(:RemoveWorkflow).with(:package, 0, "package") + expect(subject).to receive(:RemoveWorkflow).with(:package, product.installation_package_repo, "package") subject.merge_product_workflow(product) end end From ae8dc2f7a6bccb5225aebd7fa93a66fb7f2b486a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Fri, 14 Sep 2018 08:45:53 +0200 Subject: [PATCH 120/223] Changes --- package/yast2.changes | 9 +++++++++ package/yast2.spec | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index a959354a2..5f844a7d6 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Mon Sep 17 12:12:52 UTC 2018 - lslezak@suse.cz + +- Allow reading the installation.xml (skelcd-* package) from other + repository than the initial one (e.g. the self update), select + the highest version of the package (instead of the first found) + (bsc#1101016) +- 4.1.13 + ------------------------------------------------------------------- Mon Sep 17 11:21:58 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 969ae6a8b..6b711722a 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.12 +Version: 4.1.13 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From ac110a1bc4c9ea8e8d45b76d03aa8af968b1048e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Fri, 14 Sep 2018 10:16:17 +0100 Subject: [PATCH 121/223] Added firewalld system_service --- .../network/src/lib/y2firewall/firewalld.rb | 10 +++++++++ .../network/test/y2firewall/firewalld_test.rb | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/library/network/src/lib/y2firewall/firewalld.rb b/library/network/src/lib/y2firewall/firewalld.rb index 79c94b673..ee9b1c2d5 100644 --- a/library/network/src/lib/y2firewall/firewalld.rb +++ b/library/network/src/lib/y2firewall/firewalld.rb @@ -28,6 +28,7 @@ require "y2firewall/firewalld/zone" require "y2firewall/firewalld/zone_reader" require "y2firewall/firewalld/service_reader" +require "yast2/system_service" require "singleton" Yast.import "PackageSystem" @@ -224,10 +225,19 @@ def read? end # Convenience method to instantiate the firewalld API + # + # @return [Y2Firewall::Firewalld::Api] def api @api ||= Api.new end + # Convenience method to instantiate the firewalld system service + # + # @return [Yast2::SystemService, nil] + def system_service + @system_service ||= Yast2::SystemService.find(SERVICE) + end + private # Convenience method to instantiate a new zone reader diff --git a/library/network/test/y2firewall/firewalld_test.rb b/library/network/test/y2firewall/firewalld_test.rb index e723085f0..7a9a58472 100755 --- a/library/network/test/y2firewall/firewalld_test.rb +++ b/library/network/test/y2firewall/firewalld_test.rb @@ -196,6 +196,27 @@ end end + describe "#system_service" do + let(:service) { Yast2::SystemService.build(Y2Firewall::Firewalld::SERVICE) } + before do + allow(Yast2::SystemService).to receive(:find).and_return(service) + end + + context "if the firewalld service is found" do + it "returns the firewalld Yast2::SystemService object" do + expect(firewalld.system_service).to be_a Yast2::SystemService + end + end + + context "if the firewalld service is not found" do + let(:service) { nil } + + it "returns nil" do + expect(firewalld.system_service).to eq(nil) + end + end + end + describe "#read" do let(:zones_definition) do ["dmz", From af0b48965c16d5f10d5a8fd6f04b55f396aa003c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Fri, 14 Sep 2018 15:10:42 +0100 Subject: [PATCH 122/223] Bump version & changelog --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 5f844a7d6..4cfe6a253 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Sep 18 08:30:51 UTC 2018 - knut.anderssen@suse.com + +- Y2Firewall::Firewalld: Added convenience method to obtain the + firewalld service object (fate#324662) +- 4.1.14 + ------------------------------------------------------------------- Mon Sep 17 12:12:52 UTC 2018 - lslezak@suse.cz diff --git a/package/yast2.spec b/package/yast2.spec index 6b711722a..12f8e472c 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.13 +Version: 4.1.14 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 6a39fcaffa22b33be09628f508bdb35654f3f457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 18 Sep 2018 09:27:02 +0100 Subject: [PATCH 123/223] Add a CWM::Popup widget class * Extracted from yast2-storage-ng. --- library/cwm/src/Makefile.am | 1 + library/cwm/src/lib/cwm/popup.rb | 104 +++++++++++++++++++++++++++++++ library/cwm/test/Makefile.am | 1 + library/cwm/test/popup_test.rb | 63 +++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 library/cwm/src/lib/cwm/popup.rb create mode 100644 library/cwm/test/popup_test.rb diff --git a/library/cwm/src/Makefile.am b/library/cwm/src/Makefile.am index 93dfc0972..6a612701f 100644 --- a/library/cwm/src/Makefile.am +++ b/library/cwm/src/Makefile.am @@ -18,6 +18,7 @@ ycwm_DATA = \ lib/cwm/dialog.rb \ lib/cwm/page.rb \ lib/cwm/pager.rb \ + lib/cwm/popup.rb \ lib/cwm/replace_point.rb \ lib/cwm/rspec.rb \ lib/cwm/table.rb \ diff --git a/library/cwm/src/lib/cwm/popup.rb b/library/cwm/src/lib/cwm/popup.rb new file mode 100644 index 000000000..47ed6b657 --- /dev/null +++ b/library/cwm/src/lib/cwm/popup.rb @@ -0,0 +1,104 @@ +# encoding: utf-8 + +# Copyright (c) [2018] 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/dialog" + +module CWM + # CWM pop-up dialog + # + # This class offers a CWM dialog which behaves as a pop-up. + class Popup < Dialog + # Determines that a dialog should always be open + # + # @return [true] + # + # @see CWM::Dialog#wizard_create_dialog + def should_open_dialog? + true + end + + private + + # Redefines the mechanism to open the dialog to use the adapted layout + # + # @param block [Proc] + # @see CWM::Dialog#wizard_create_dialog + def wizard_create_dialog(&block) + Yast::UI.OpenDialog(layout) + block.call + ensure + Yast::UI.CloseDialog + end + + # Defines the dialog's layout + # + # @return [Yast::Term] + def layout + VBox( + HSpacing(50), + Left(Heading(Id(:title), title)), + VStretch(), + VSpacing(1), + MinSize(min_width, min_height, ReplacePoint(Id(:contents), Empty())), + VSpacing(1), + VStretch(), + ButtonBox(*buttons) + ) + end + + # Popup min width + # + # @return [Integer] + def min_width + 1 + end + + # Popup min height + # + # @return [Integer] + def min_height + 1 + end + + def ok_button_label + Yast::Label.OKButton + end + + def cancel_button_label + Yast::Label.CancelButton + end + + def buttons + [help_button, ok_button, cancel_button] + end + + def help_button + PushButton(Id(:help), Opt(:helpButton), Yast::Label.HelpButton) + end + + def ok_button + PushButton(Id(:ok), Opt(:default), ok_button_label) + end + + def cancel_button + PushButton(Id(:cancel), cancel_button_label) + end + end +end diff --git a/library/cwm/test/Makefile.am b/library/cwm/test/Makefile.am index 438fd6924..680850100 100644 --- a/library/cwm/test/Makefile.am +++ b/library/cwm/test/Makefile.am @@ -4,6 +4,7 @@ TESTS = \ custom_widget_test.rb \ dialog_test.rb \ pager_test.rb \ + popup_test.rb \ replace_point_test.rb \ table_test.rb \ tree_test.rb \ diff --git a/library/cwm/test/popup_test.rb b/library/cwm/test/popup_test.rb new file mode 100644 index 000000000..bbfbe1ba8 --- /dev/null +++ b/library/cwm/test/popup_test.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2018] 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_relative "test_helper" + +require "cwm/popup" +require "cwm/rspec" + +describe CWM::Popup do + class TestCWMPopup < CWM::Popup + def contents + VBox() + end + end + + subject { TestCWMPopup.new } + + include_examples "CWM::Dialog" + + describe ".run" do + before do + allow(Yast::Wizard).to receive(:IsWizardDialog).and_return(wizard_dialog?) + allow(Yast::CWM).to receive(:show).and_return(:launch) + end + + context "when running on a wizard" do + let(:wizard_dialog?) { true } + + it "always opens a dialog" do + expect(Yast::UI).to receive(:OpenDialog).with(Yast::Term) + TestCWMPopup.run + end + end + + context "when not running on a wizard" do + let(:wizard_dialog?) { false } + + it "always opens a dialog" do + expect(Yast::UI).to receive(:OpenDialog).with(Yast::Term) + TestCWMPopup.run + end + end + end +end From 075699a4a74c6f5759cb88e266d17e726bd79cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 18 Sep 2018 09:53:32 +0100 Subject: [PATCH 124/223] Add a class to keep UI state * Extracted from yast2-storage-ng. --- library/cwm/src/Makefile.am | 1 + library/cwm/src/lib/cwm/ui_state.rb | 209 ++++++++++++++++++++++++++++ library/cwm/test/ui_state_test.rb | 177 +++++++++++++++++++++++ 3 files changed, 387 insertions(+) create mode 100644 library/cwm/src/lib/cwm/ui_state.rb create mode 100644 library/cwm/test/ui_state_test.rb diff --git a/library/cwm/src/Makefile.am b/library/cwm/src/Makefile.am index 6a612701f..5bd516443 100644 --- a/library/cwm/src/Makefile.am +++ b/library/cwm/src/Makefile.am @@ -25,6 +25,7 @@ ycwm_DATA = \ lib/cwm/tabs.rb \ lib/cwm/tree.rb \ lib/cwm/tree_pager.rb \ + lib/cwm/ui_state.rb \ lib/cwm/widget.rb \ lib/cwm/wrapper_widget.rb diff --git a/library/cwm/src/lib/cwm/ui_state.rb b/library/cwm/src/lib/cwm/ui_state.rb new file mode 100644 index 000000000..22fd70ad0 --- /dev/null +++ b/library/cwm/src/lib/cwm/ui_state.rb @@ -0,0 +1,209 @@ +# encoding: utf-8 + +# Copyright (c) [2018] 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 "abstract_method" + +module CWM + # Singleton class to keep the position of the user in the UI and other similar + # information that needs to be rememberd across UI redraws to give the user a + # sense of continuity. + # + # If you want to see it in action, have a look at yast2-storage-ng or yast2-firewall + # modules. + # + # @example Defining a UIState class to handle firewall zones (extracted from yast2-firewall) + # + # require "cwm/ui_state" + # class MyUIState < CWM::UIState + # def go_to_tree_node(page) + # super + # self.candidate_notes = + # if page.respond_to?(:zone) + # zone_page_candidates(page) + # else + # [page.label] + # end + # end + # end + # + # private + # + # def zone_page_candidates(page) + # [page.zone.name] + # end + # end + # + # @example Using the UIState from an CWM::TreePager + # + # class OverviewTreePager < CWM::TreePager + # # Overrides default behaviour to register the new state. + # def switch_page(page) + # MyUIState.instance.go_to_tree_node(page) + # super + # end + # + # # Ensures the tree is properly initialized according to the UI state after + # # a redraw + # def initial_page + # MyUIState.instance.find_tree_node(@pages) || super + # end + # end + # + # @example Registering the selected row from a CWM::Table + # + # class MyTable < ::CWM::Table + # def opt + # [:notify, :immediate] + # end + # + # def handle(event) + # MyUIState.instance.select_row(value) if event["EventReason"] == "SelectionChanged" + # nil + # end + # end + # + class UIState + include Yast::I18n + + # Constructor + # + # Called through {.create_instance}, starts with a blank situation (which + # means default for each widget will be honored). + def initialize + textdomain(textdomain_name) + @candidate_nodes = [] + end + + # Specify textdomain name + # + # @return [String] Textdomain (e.g., "firewall") + abstract_method :textdomain_name + + # Method to be called when the user decides to visit a given page by + # clicking in one node of the general tree. + # + # It remembers the decision so the user is taken back to a sensible point of + # the tree (very often the last he decided to visit) after redrawing. + # + # @param [CWM::Page] page associated to the tree node + def go_to_tree_node(page) + self.candidate_nodes = [page.label] + + # Landing in a new node, so invalidate previous details about position + # within a node, they no longer apply + self.tab = nil + end + + # Method to be called when the user switches to a tab within a tree node. + # + # It remembers the decision so the same tab is showed in case the user stays + # in the same node after redrawing. + # + # @param [CWM::Page] page associated to the tab + def switch_to_tab(page) + self.tab = page.label + end + + # Method to be called when the user operates in a row of a table. + # + # @param row_id [Object] row identifier + def select_row(row_id) + self.row_id = row_id + end + + # Select the page to open in the general tree after a redraw + # + # @param pages [Array] all the pages in the tree + # @return [CWM::Page, nil] + def find_tree_node(pages) + candidate_nodes.each.with_index do |candidate, idx| + result = pages.find { |page| matches?(page, candidate) } + if result + # If we had to use one of the fallbacks, the tab name is not longer + # trustworthy + self.tab = nil unless idx.zero? + return result + end + end + self.tab = nil + nil + end + + # Select the tab to open within the node after a redraw + # + # @param pages [Array] pages for all the possible tabs + # @return [CWM::Page, nil] + def find_tab(pages) + return nil unless tab + pages.find { |page| page.label == tab } + end + + # @!attribute [r] row_id + # @return [Object] Selected row identifier + # @see #row_id + attr_reader :row_id + + protected + + attr_writer :row_id + + # Where to place the user within the general tree in next redraw + # @return [Array] + attr_accessor :candidate_nodes + + # Concrete tab within the current node to show in the next redraw + # @return [String, nil] + attr_reader :tab + # @see #tab + def tab=(tab) + @tab = tab + # If the user switched to a new tab, invalidate details about the inner + # table + self.row_id = nil + end + + # Whether the given page matches with the candidate tree node + # + # @param page [CWM::Page] + # @param candidate [Integer, String] + # @return boolean + def matches?(page, candidate) + page.label == candidate + end + + class << self + # Singleton instance + def instance + create_instance unless @instance + @instance + end + + # Enforce a new clean instance + def create_instance + @instance = new + end + + # Make sure only .instance and .create_instance can be used to + # create objects + private :new, :allocate + end + end +end diff --git a/library/cwm/test/ui_state_test.rb b/library/cwm/test/ui_state_test.rb new file mode 100644 index 000000000..5fd0a4e0a --- /dev/null +++ b/library/cwm/test/ui_state_test.rb @@ -0,0 +1,177 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2017] 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_relative "test_helper" +require "cwm/ui_state" + +describe CWM::UIState do + class MyUIState < CWM::UIState + def textdomain_name + "base" + end + end + subject(:ui_state) { MyUIState.instance } + + describe ".new" do + it "cannot be used directly" do + expect { MyUIState.new }.to raise_error(/private method/) + end + end + + describe ".instance" do + it "returns the singleton object in subsequent calls" do + initial = MyUIState.create_instance + second = MyUIState.instance + # Note using equal to ensure is actually the same object (same object_id) + expect(second).to equal initial + expect(MyUIState.instance).to equal initial + end + end + + describe ".create_instance" do + it "returns a new singleton UIState object" do + initial = MyUIState.instance + result = MyUIState.create_instance + expect(result).to be_a MyUIState + expect(result).to_not equal initial + end + end + + describe "#find_tree_node" do + let(:pager) { double("TreePager") } + let(:page1) { double("Page", label: "Page 1") } + let(:page2) { double("Page", label: "Page 2") } + + let(:pages) { [page1, page2] } + + context "if the user has still not visited any node" do + before { MyUIState.create_instance } + + it "returns nil" do + expect(ui_state.find_tree_node(pages)).to be_nil + end + end + + context "when the user has opened a page" do + before { ui_state.go_to_tree_node(page2) } + + context "if the page is still there after redrawing" do + + it "selects the previously selected page" do + expect(ui_state.find_tree_node(pages)).to eq page2 + end + end + + context "if the page is not longer there after redrawing" do + before do + pages.clear + pages.concat [page1] + end + + it "returns nil" do + expect(ui_state.find_tree_node(pages)).to be_nil + end + end + end + + describe "#find_tab" do + let(:pager) { double("TreePager") } + let(:tab1) { double("tab1", label: "Tab 1") } + let(:tab2) { double("tab2", label: "Tab 2") } + let(:tabs) { [tab1, tab2] } + + context "if the user has still not clicked in any tab" do + before { MyUIState.create_instance } + + it "returns nil" do + expect(ui_state.find_tab(tabs)).to be_nil + end + end + + context "if the user has switched to a tab in the current tree node" do + before { ui_state.switch_to_tab(tab2) } + + it "selects the corresponding page" do + expect(ui_state.find_tab(tabs)).to eq tab2 + end + end + + context "if the switched to a tab but then moved to a different tree node" do + let(:another_page) { double("Page", label: "A section") } + + before do + ui_state.switch_to_tab(tab2) + ui_state.go_to_tree_node(another_page) + end + + it "returns nil even if there is another tab with the same label" do + expect(ui_state.find_tab(tabs)).to be_nil + end + end + end + + describe "#row_id" do + let(:row_id) { "my-row" } + context "if the user has still not selected any row" do + before { MyUIState.create_instance } + + it "returns nil" do + expect(ui_state.row_id).to be_nil + end + end + + context "if the user has selected a row" do + before { ui_state.select_row(row_id) } + + it "returns the row id" do + expect(ui_state.row_id).to eq(row_id) + end + end + + context "if the user had selected a row but then moved to a different tab" do + let(:another_tab) { double("Tab", label: "A tab") } + + before do + ui_state.select_row(row_id) + ui_state.switch_to_tab(another_tab) + end + + it "returns nil" do + expect(ui_state.row_id).to be_nil + end + end + + context "if the user had selected a row but then moved to a different tree node" do + let(:another_page) { double("Page", label: "Somewhere") } + + before do + ui_state.select_row(row_id) + ui_state.go_to_tree_node(another_page) + end + + it "returns nil" do + expect(ui_state.row_id).to be_nil + end + end + end + end +end From 6b54fe3f38846d3d133ff1e6955d3612b43ae0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 18 Sep 2018 13:20:21 +0100 Subject: [PATCH 125/223] Bump version and update changes file --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index cc9b134b6..aa5990b97 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Sep 18 08:28:02 UTC 2018 - igonzalezsosa@suse.com + +- Add a new popup widget (fate#324662). +- Add a helper class UIState to keep the UI states when using CWM. +- 4.0.94 + ------------------------------------------------------------------- Mon Sep 17 11:52:51 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index e7b451369..ac51aacf5 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.93 +Version: 4.0.94 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 2bfa8c6fa2971f87916ae3ac7e7ed44aec8d7a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 18 Sep 2018 16:43:01 +0100 Subject: [PATCH 126/223] Remove not needed call to textdomain --- library/cwm/src/lib/cwm/ui_state.rb | 6 ------ library/cwm/test/ui_state_test.rb | 27 +++++++++++---------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/library/cwm/src/lib/cwm/ui_state.rb b/library/cwm/src/lib/cwm/ui_state.rb index 22fd70ad0..10502b5be 100644 --- a/library/cwm/src/lib/cwm/ui_state.rb +++ b/library/cwm/src/lib/cwm/ui_state.rb @@ -88,15 +88,9 @@ class UIState # Called through {.create_instance}, starts with a blank situation (which # means default for each widget will be honored). def initialize - textdomain(textdomain_name) @candidate_nodes = [] end - # Specify textdomain name - # - # @return [String] Textdomain (e.g., "firewall") - abstract_method :textdomain_name - # Method to be called when the user decides to visit a given page by # clicking in one node of the general tree. # diff --git a/library/cwm/test/ui_state_test.rb b/library/cwm/test/ui_state_test.rb index 5fd0a4e0a..831d554c6 100644 --- a/library/cwm/test/ui_state_test.rb +++ b/library/cwm/test/ui_state_test.rb @@ -24,34 +24,29 @@ require "cwm/ui_state" describe CWM::UIState do - class MyUIState < CWM::UIState - def textdomain_name - "base" - end - end - subject(:ui_state) { MyUIState.instance } + subject(:ui_state) { described_class.instance } describe ".new" do it "cannot be used directly" do - expect { MyUIState.new }.to raise_error(/private method/) + expect { described_class.new }.to raise_error(/private method/) end end describe ".instance" do it "returns the singleton object in subsequent calls" do - initial = MyUIState.create_instance - second = MyUIState.instance + initial = described_class.create_instance + second = described_class.instance # Note using equal to ensure is actually the same object (same object_id) expect(second).to equal initial - expect(MyUIState.instance).to equal initial + expect(described_class.instance).to equal initial end end describe ".create_instance" do it "returns a new singleton UIState object" do - initial = MyUIState.instance - result = MyUIState.create_instance - expect(result).to be_a MyUIState + initial = described_class.instance + result = described_class.create_instance + expect(result).to be_a described_class expect(result).to_not equal initial end end @@ -64,7 +59,7 @@ def textdomain_name let(:pages) { [page1, page2] } context "if the user has still not visited any node" do - before { MyUIState.create_instance } + before { described_class.create_instance } it "returns nil" do expect(ui_state.find_tree_node(pages)).to be_nil @@ -100,7 +95,7 @@ def textdomain_name let(:tabs) { [tab1, tab2] } context "if the user has still not clicked in any tab" do - before { MyUIState.create_instance } + before { described_class.create_instance } it "returns nil" do expect(ui_state.find_tab(tabs)).to be_nil @@ -132,7 +127,7 @@ def textdomain_name describe "#row_id" do let(:row_id) { "my-row" } context "if the user has still not selected any row" do - before { MyUIState.create_instance } + before { described_class.create_instance } it "returns nil" do expect(ui_state.row_id).to be_nil From 6cc448ed86dd41371e11507b1a732de07180e9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 18 Sep 2018 16:43:37 +0100 Subject: [PATCH 127/223] Fix tests permissions --- library/cwm/test/popup_test.rb | 0 library/cwm/test/test_helper.rb | 0 library/cwm/test/ui_state_test.rb | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 library/cwm/test/popup_test.rb mode change 100644 => 100755 library/cwm/test/test_helper.rb mode change 100644 => 100755 library/cwm/test/ui_state_test.rb diff --git a/library/cwm/test/popup_test.rb b/library/cwm/test/popup_test.rb old mode 100644 new mode 100755 diff --git a/library/cwm/test/test_helper.rb b/library/cwm/test/test_helper.rb old mode 100644 new mode 100755 diff --git a/library/cwm/test/ui_state_test.rb b/library/cwm/test/ui_state_test.rb old mode 100644 new mode 100755 From 2df7d1239221bb571eebadfea353449446880fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 18 Sep 2018 16:47:33 +0100 Subject: [PATCH 128/223] Add UIState test to Makefile.am --- library/cwm/test/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/library/cwm/test/Makefile.am b/library/cwm/test/Makefile.am index 680850100..2ac82a974 100644 --- a/library/cwm/test/Makefile.am +++ b/library/cwm/test/Makefile.am @@ -9,6 +9,7 @@ TESTS = \ table_test.rb \ tree_test.rb \ tree_pager_test.rb \ + ui_state_test.rb \ wrapper_widget_test.rb TEST_EXTENSIONS = .rb From 8689b2250deecc0c7520ead9da9ea3641f993d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 19 Sep 2018 08:52:09 +0100 Subject: [PATCH 129/223] Added Y2Firewall::Firewalld::Interface class --- .../src/lib/y2firewall/firewalld/interface.rb | 85 +++++++++++++++++++ .../src/lib/y2firewall/helpers/interfaces.rb | 28 ++---- .../src/modules/CWMFirewallInterfaces.rb | 10 +-- .../test/cwm_firewall_interfaces_test.rb | 17 ++-- library/network/test/test_helper.rb | 5 ++ .../y2firewall/helpers/interfaces_test.rb | 16 ++-- 6 files changed, 116 insertions(+), 45 deletions(-) create mode 100644 library/network/src/lib/y2firewall/firewalld/interface.rb diff --git a/library/network/src/lib/y2firewall/firewalld/interface.rb b/library/network/src/lib/y2firewall/firewalld/interface.rb new file mode 100644 index 000000000..5a04d248e --- /dev/null +++ b/library/network/src/lib/y2firewall/firewalld/interface.rb @@ -0,0 +1,85 @@ +# encoding: utf-8 + +# ------------------------------------------------------------------------------ +# Copyright (c) 2018 SUSE LLC +# +# +# 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. +# +# To contact SUSE about this file by physical or electronic mail, you may find +# current contact information at www.suse.com. +# ------------------------------------------------------------------------------ + +require "yast" +require "y2firewall/firewalld" + +module Y2Firewall + class Firewalld + # Class to work with firewalld interfaces + class Interface + attr_accessor :id + + def self.build(name) + new(name) + end + + # Return an array with all the known or configured interfaces + # + # @return [Array] known interfaces + def self.known + Yast.import "NetworkInterfaces" + interfaces = Yast::NetworkInterfaces.List("").reject { |i| i == "lo" } + + interfaces.map { |i| build(i) } + end + + # @return [String] interface name + def name + id.to_s + end + + # @return [String] device name + def device_name + Yast::NetworkInterfaces.GetValue(name, "NAME") + end + + # Return the zone name for a given interface from the firewalld instance + # instead of from the API. + # + # @param name [String] interface name + # @return [String, nil] zone name whether belongs to some or nil if not + def zone + zone = fw.zones.find { |z| z.interfaces.include?(id.to_s) } + + zone ? zone.name : nil + end + + private + + # Constructor + # + # @param name [String] interface name + def initialize(name) + Yast.import "NetworkInterfaces" + + @id = name.to_sym + end + + # Return an instance of Y2Firewall::Firewalld + # + # @return [Y2Firewall::Firewalld] a firewalld instance + def fw + Y2Firewall::Firewalld.instance + end + end + end +end diff --git a/library/network/src/lib/y2firewall/helpers/interfaces.rb b/library/network/src/lib/y2firewall/helpers/interfaces.rb index d49d2d876..9a95e2a2f 100644 --- a/library/network/src/lib/y2firewall/helpers/interfaces.rb +++ b/library/network/src/lib/y2firewall/helpers/interfaces.rb @@ -22,7 +22,7 @@ # # *************************************************************************** require "yast" -require "y2firewall/firewalld" +require "y2firewall/firewalld/interface" module Y2Firewall module Helpers @@ -44,7 +44,7 @@ def firewalld # # @return [Array] default zone interface names def default_interfaces - known_interfaces.select { |i| i["zone"].to_s.empty? }.map { |i| i["id"] } + known_interfaces.select { |i| !i.zone }.map(&:name) end # Return the zone name for a given interface from the firewalld instance @@ -65,31 +65,13 @@ def default_zone @default_zone ||= firewalld.find_zone(firewalld.default_zone) end - # Return a hash of all the known interfaces with their "id", "name" and - # "zone". + # Return an array with all the known or configured interfaces # - # @example - # CWMFirewallInterfaces.known_interfaces #=> - # [ - # { "id" => "eth0", "name" => "Intel Ethernet Connection I217-LM", "zone" => "external"}, - # { "id" => "eth1", "name" => "Intel Ethernet Connection I217-LM", "zone" => "public"}, - # { "id" => "eth2", "name" => "Intel Ethernet Connection I217-LM", "zone" => nil}, - # { "id" => "eth3", "name" => "Intel Ethernet Connection I217-LM", "zone" => nil}, - # ] - # - # @return [Array>] known interfaces "id", "name" and "zone" + # @return [Array] known interfaces def known_interfaces return @known_interfaces if @known_interfaces - interfaces = Yast::NetworkInterfaces.List("").reject { |i| i == "lo" } - - @known_interfaces = interfaces.map do |interface| - { - "id" => interface, - "name" => Yast::NetworkInterfaces.GetValue(interface, "NAME"), - "zone" => interface_zone(interface) - } - end + @known_interfaces = Y2Firewall::Firewalld::Interface.known end end end diff --git a/library/network/src/modules/CWMFirewallInterfaces.rb b/library/network/src/modules/CWMFirewallInterfaces.rb index 652741b37..52c0032a1 100644 --- a/library/network/src/modules/CWMFirewallInterfaces.rb +++ b/library/network/src/modules/CWMFirewallInterfaces.rb @@ -250,19 +250,19 @@ def StoreAllowedInterfaces(services) zones = known_interfaces.each_with_object([]) do |known_interface, a| - if allowed_interfaces.include?(known_interface["id"]) - zone_name = known_interface["zone"] || default_zone.name + if allowed_interfaces.include?(known_interface.name) + zone_name = known_interface.zone || firewalld.default_zone a << zone_name end end - firewalld.zones.map do |zone| + firewalld.zones.each do |zone| if zones.include?(zone.name) - services.map do |service| + services.each do |service| zone.add_service(service) unless zone.services.include?(service) end else - services.map do |service| + services.each do |service| zone.remove_service(service) if zone.services.include?(service) end end diff --git a/library/network/test/cwm_firewall_interfaces_test.rb b/library/network/test/cwm_firewall_interfaces_test.rb index 7dbe09c14..eda49479f 100755 --- a/library/network/test/cwm_firewall_interfaces_test.rb +++ b/library/network/test/cwm_firewall_interfaces_test.rb @@ -201,9 +201,9 @@ describe "#Selected2Opened" do let(:known_interfaces) do [ - { "id" => "eth0", "name" => "Ethernet 1", "zone" => "external" }, - { "id" => "eth1", "name" => "Ethernet 2", "zone" => "public" }, - { "id" => "eth2", "name" => "Ethernet 3", "zone" => "dmz" } + mock_firewalld_interface(:eth0, "Ethernet 1", "external"), + mock_firewalld_interface(:eth1, "Ethernet 2", "public"), + mock_firewalld_interface(:eth2, "Ethernet 3", "dmz") ] end @@ -230,9 +230,9 @@ describe "#StoreAllowedInterfaces" do let(:known_interfaces) do [ - { "id" => "eth0", "name" => "Ethernet 1", "zone" => "external" }, - { "id" => "eth1", "name" => "Ethernet 2", "zone" => "public" }, - { "id" => "eth2", "name" => "Ethernet 3", "zone" => nil } + mock_firewalld_interface(:eth0, "Ethernet 1", "external"), + mock_firewalld_interface(:eth1, "Ethernet 2", "public"), + mock_firewalld_interface(:eth2, "Ethernet 3", "nil") ] end @@ -249,10 +249,9 @@ let(:zones) { [external_zone, public_zone] } before do - expect(subject).to receive(:known_interfaces).and_return(known_interfaces) - expect(subject).to receive(:allowed_interfaces).and_return(["eth0", "eth1"]) + allow(subject).to receive(:known_interfaces).and_return(known_interfaces) allow(firewalld).to receive(:zones).and_return(zones) - allow(subject).to receive(:default_zone).and_return(public_zone) + allow(firewalld).to receive(:default_zone).and_return("public") allow(subject).to receive(:configuration_changed).and_return(true) allow(subject).to receive(:allowed_interfaces).and_return(["eth0", "eth1", "eth2"]) end diff --git a/library/network/test/test_helper.rb b/library/network/test/test_helper.rb index 02b187836..a62c678c2 100644 --- a/library/network/test/test_helper.rb +++ b/library/network/test/test_helper.rb @@ -81,3 +81,8 @@ module NetworkStubs } }.freeze end + +def mock_firewalld_interface(id, name, zone) + instance_double("Y2Firewall::Firewalld::Interface", + id: id, name: id.to_s, device_name: name, zone: zone) +end diff --git a/library/network/test/y2firewall/helpers/interfaces_test.rb b/library/network/test/y2firewall/helpers/interfaces_test.rb index 4c28c8141..1b4a3611b 100755 --- a/library/network/test/y2firewall/helpers/interfaces_test.rb +++ b/library/network/test/y2firewall/helpers/interfaces_test.rb @@ -35,14 +35,14 @@ class DummyClass end describe "#known_interfaces" do - it "returns a hash with the 'id', 'name' and zone of the current interfaces" do - expect(subject.known_interfaces) - .to eql( - [ - { "id" => "eth0", "name" => "Intel I217-LM", "zone" => "external" }, - { "id" => "eth1", "name" => "Intel I217-LM", "zone" => nil } - ] - ) + it "returns an array with the known firewalld interfaces" do + known_interfaces = subject.known_interfaces + expect(known_interfaces.size).to eql(2) + eth0 = known_interfaces.find { |i| i.id == :eth0 } + expect(eth0.name).to eq("eth0") + expect(eth0.zone).to eq("external") + expect(eth0.device_name).to eq("Intel I217-LM") + expect(eth0).to be_a(Y2Firewall::Firewalld::Interface) end end From 9ed7614258444fc1687dacf45bea6d3c0b6c51ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 19 Sep 2018 10:33:07 +0100 Subject: [PATCH 130/223] Changes based on CR. --- library/network/src/Makefile.am | 1 + .../src/lib/y2firewall/firewalld/interface.rb | 22 ++++++++----------- .../src/lib/y2firewall/helpers/interfaces.rb | 4 +--- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/library/network/src/Makefile.am b/library/network/src/Makefile.am index 64597e0b1..53368aa65 100644 --- a/library/network/src/Makefile.am +++ b/library/network/src/Makefile.am @@ -42,6 +42,7 @@ yfwlib_DATA = \ yfwdlibdir = @ylibdir@/y2firewall/firewalld yfwdlib_DATA = \ lib/y2firewall/firewalld/api.rb \ + lib/y2firewall/firewalld/interface.rb \ lib/y2firewall/firewalld/relations.rb \ lib/y2firewall/firewalld/service.rb \ lib/y2firewall/firewalld/zone.rb \ diff --git a/library/network/src/lib/y2firewall/firewalld/interface.rb b/library/network/src/lib/y2firewall/firewalld/interface.rb index 5a04d248e..636cf8179 100644 --- a/library/network/src/lib/y2firewall/firewalld/interface.rb +++ b/library/network/src/lib/y2firewall/firewalld/interface.rb @@ -26,10 +26,16 @@ module Y2Firewall class Firewalld # Class to work with firewalld interfaces class Interface + # @return [Symbol] attr_accessor :id - def self.build(name) - new(name) + # Constructor + # + # @param name [String] interface name + def initialize(name) + Yast.import "NetworkInterfaces" + + @id = name.to_sym end # Return an array with all the known or configured interfaces @@ -39,7 +45,7 @@ def self.known Yast.import "NetworkInterfaces" interfaces = Yast::NetworkInterfaces.List("").reject { |i| i == "lo" } - interfaces.map { |i| build(i) } + interfaces.map { |i| new(i) } end # @return [String] interface name @@ -55,7 +61,6 @@ def device_name # Return the zone name for a given interface from the firewalld instance # instead of from the API. # - # @param name [String] interface name # @return [String, nil] zone name whether belongs to some or nil if not def zone zone = fw.zones.find { |z| z.interfaces.include?(id.to_s) } @@ -65,15 +70,6 @@ def zone private - # Constructor - # - # @param name [String] interface name - def initialize(name) - Yast.import "NetworkInterfaces" - - @id = name.to_sym - end - # Return an instance of Y2Firewall::Firewalld # # @return [Y2Firewall::Firewalld] a firewalld instance diff --git a/library/network/src/lib/y2firewall/helpers/interfaces.rb b/library/network/src/lib/y2firewall/helpers/interfaces.rb index 9a95e2a2f..8d4a915fb 100644 --- a/library/network/src/lib/y2firewall/helpers/interfaces.rb +++ b/library/network/src/lib/y2firewall/helpers/interfaces.rb @@ -53,9 +53,7 @@ def default_interfaces # @param name [String] interface name # @return [String, nil] zone name whether belongs to some or nil if not def interface_zone(name) - zone = firewalld.zones.find { |z| z.interfaces.include?(name) } - - zone ? zone.name : nil + Y2Firewall::Firewalld::Interface.new(name).zone end # Convenience method to return the default zone object From b22233c1fbd09253304ce50dbc3d47c8e925b473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 19 Sep 2018 11:06:53 +0100 Subject: [PATCH 131/223] Bump version & changelog. --- package/yast2.changes | 9 +++++++++ package/yast2.spec | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index aa5990b97..4d937575f 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Wed Sep 19 09:53:09 UTC 2018 - knut.anderssen@suse.com + +- Network (Firewall) + - Added Y2Firewall::Firewalld::Interface class. + - Adapted interfaces helpers to work with the new class. + (fate#324662) +- 4.0.95 + ------------------------------------------------------------------- Tue Sep 18 08:28:02 UTC 2018 - igonzalezsosa@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index ac51aacf5..ae462b175 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.94 +Version: 4.0.95 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 61510a8fc3c51851cdcf94b6a126cc2f02d8adce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 19 Sep 2018 11:48:01 +0100 Subject: [PATCH 132/223] Some changes based on CR. --- library/network/src/lib/y2firewall/helpers/interfaces.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/network/src/lib/y2firewall/helpers/interfaces.rb b/library/network/src/lib/y2firewall/helpers/interfaces.rb index 8d4a915fb..09277d0f8 100644 --- a/library/network/src/lib/y2firewall/helpers/interfaces.rb +++ b/library/network/src/lib/y2firewall/helpers/interfaces.rb @@ -44,7 +44,7 @@ def firewalld # # @return [Array] default zone interface names def default_interfaces - known_interfaces.select { |i| !i.zone }.map(&:name) + known_interfaces.reject(&:zone).map(&:name) end # Return the zone name for a given interface from the firewalld instance @@ -63,7 +63,8 @@ def default_zone @default_zone ||= firewalld.find_zone(firewalld.default_zone) end - # Return an array with all the known or configured interfaces + # Return an array with all the known (sysconfig configured) firewalld + # interfaces. # # @return [Array] known interfaces def known_interfaces From 76c0b3e33002c7a1fd623d2b40e5e4a5a4854bca Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 1 Aug 2018 11:25:55 +0200 Subject: [PATCH 133/223] dropped NetworkInterfaces#device_type --- library/network/src/modules/NetworkInterfaces.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index ae80df873..fa26b128c 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -191,15 +191,6 @@ def ifcfg_part(ifcfg, part) ret.nil? ? "" : ret end - # Return a device type - # @param [String] dev device - # @return device type - # @example device_type("eth1") -> "eth" - # @example device_type("eth-pcmcia-0") -> "eth" - def device_type(dev) - ifcfg_part(dev, "1") - end - # Detects a subtype of Ethernet device type according /sys or /proc content # # @example @@ -342,7 +333,9 @@ def GetTypeFromIfcfgOrName(dev, ifcfg) type = GetTypeFromIfcfg(ifcfg) if IsEmpty(type) - type = device_type(dev) if type.nil? + # last instance - no record in sysfs, no configuration, device + # name is not bounded to a type -> use fallbac "eth" as wicked already does + type = "eth" if type.nil? log.debug("GetTypeFromIfcfgOrName: device='#{dev}' type='#{type}'") From 8e098505740b787263ddef58feb3b29e8bddd495 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 13:45:30 +0200 Subject: [PATCH 134/223] Removed NetworkInterfaces#alias_num obsolete API used when alias identifier was part of ifcfg name --- .../network/src/modules/NetworkInterfaces.rb | 63 ++----------------- 1 file changed, 6 insertions(+), 57 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index fa26b128c..0cda2eb15 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -405,15 +405,6 @@ def device_num(dev) ifcfg_part(dev, "2") end - # Return a device alias number - # @param [String] dev device - # @return alias number - # @example alias_num("eth1#2") -> "2" - # @example alias_num("eth1#blah") -> "blah" - def alias_num(dev) - ifcfg_part(dev, "3") - end - # Create a device name from its type and number # @param [String] typ device type # @param [String] num device number @@ -828,26 +819,10 @@ def Write(devregex) # remove deleted devices log.info("Deleted=#{@Deleted}") Builtins.foreach(@Deleted) do |d| - anum = alias_num(d) - if anum == "" - # delete config file - p = Builtins.add(path(".network.section"), d) - log.debug("deleting: #{p}") - SCR.Write(p, nil) - else - dev = device_name_from_alias(d) - typ = GetType(dev) - base = Builtins.add(path(".network.value"), dev) - # look in OriginalDevs because we need to catch all variables - # of the alias - - dev_aliases = original_devs.fetch(typ, {}).fetch(dev, {}).fetch("_aliases", {}) - dev_aliases.fetch(anum, {}).keys.each do |key| - p = base + "#{key}_#{anum}" - log.debug("deleting: #{p}") - SCR.Write(p, nil) - end - end + # delete config file + p = Builtins.add(path(".network.section"), d) + log.debug("deleting: #{p}") + SCR.Write(p, nil) end @Deleted = [] @@ -1361,7 +1336,6 @@ def Check(dev) Builtins.y2debug("Check(%1)", dev) typ = GetType(dev) # string num = device_num(dev); - # string anum = alias_num(dev); return false if !Builtins.haskey(@Devices, typ) devsmap = Ops.get(@Devices, typ, {}) @@ -1387,17 +1361,6 @@ def Select(name) @Name = name t = GetType(@Name) @Current = Ops.get(@Devices, [t, @Name], {}) - a = alias_num(@Name) - if !a.nil? && a != "" - @Current = Ops.get_map(@Current, ["_aliases", a], {}) - end - - if @Current == {} - # Default device map - @Current = - # FIXME: remaining items - {} - end Builtins.y2debug("Name=%1", @Name) Builtins.y2debug("Current=%1", @Current) @@ -1459,19 +1422,13 @@ def Change2(name, newdev, check) t = int_type if Ops.greater_than(Builtins.size(int_type), 0) end - a = alias_num(name) Builtins.y2debug("ChangeDevice(%1)", name) devsmap = Ops.get(@Devices, t, {}) devmap = Ops.get(devsmap, name, {}) amap = Ops.get_map(devmap, "_aliases", {}) - if a != "" - Ops.set(amap, a, newdev) - Ops.set(devmap, "_aliases", amap) - else - devmap = deep_copy(newdev) - end + devmap = deep_copy(newdev) Ops.set(devsmap, name, devmap) Ops.set(@Devices, t, devsmap) @@ -1487,16 +1444,9 @@ def Delete2(name) end t = GetType(name) - a = alias_num(name) devsmap = Ops.get(@Devices, t, {}) - if a != "" - amap = Ops.get_map(devsmap, [name, "_aliases"], {}) - amap = Builtins.remove(amap, a) - Ops.set(devsmap, [name, "_aliases"], amap) - else - devsmap = Builtins.remove(devsmap, name) - end + devsmap = Builtins.remove(devsmap, name) Ops.set(@Devices, t, devsmap) @@ -1747,7 +1697,6 @@ def get_devices(devregex) publish function: :GetType, type: "string (string)" publish function: :GetDeviceTypeName, type: "string (string)" publish function: :device_num, type: "string (string)" - publish function: :alias_num, type: "string (string)" publish function: :IsHotplug, type: "boolean (string)" publish function: :IsConnected, type: "boolean (string)" publish function: :RealType, type: "string (string, string)" From d6825a0b539ccde759547f8581d35cf0213e491d Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 13:52:18 +0200 Subject: [PATCH 135/223] Dropped failing old testsuite - will be replaced by rspec tests --- .../testsuite/tests/NetworkInterfaces2.err | 0 .../testsuite/tests/NetworkInterfaces2.out | 231 ------------------ .../testsuite/tests/NetworkInterfaces2.rb | 210 ---------------- 3 files changed, 441 deletions(-) delete mode 100644 library/network/testsuite/tests/NetworkInterfaces2.err delete mode 100644 library/network/testsuite/tests/NetworkInterfaces2.out delete mode 100644 library/network/testsuite/tests/NetworkInterfaces2.rb diff --git a/library/network/testsuite/tests/NetworkInterfaces2.err b/library/network/testsuite/tests/NetworkInterfaces2.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/library/network/testsuite/tests/NetworkInterfaces2.out b/library/network/testsuite/tests/NetworkInterfaces2.out deleted file mode 100644 index 4cd72ddee..000000000 --- a/library/network/testsuite/tests/NetworkInterfaces2.out +++ /dev/null @@ -1,231 +0,0 @@ -Read .target.tmpdir "/tmp" -Dump NetworkInterfaces::Read -Dir .network.section: ["arc5", "atm5", "ci5", "ctc5", "dummy5", "escon5", "eth0.3", "eth5", "eth6", "eth7", "eth8", "eth9", "fddi5", "hippi5", "hsi5", "ippp5", "iucv5", "lo", "mynet0", "myri5", "myvlantoo", "ppp5", "tr5", "tr~", "virtlan4", "vlan3"] -Dir .network.value."arc5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."arc5".BOOTPROTO "dhcp" -Read .network.value."arc5".STARTMODE "manual" -Read .target.stat "/sys/class/net/arc5/type" nil -Read .target.string "/sys/class/net/arc5/type" nil -Dir .network.value."atm5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."atm5".BOOTPROTO "dhcp" -Read .network.value."atm5".STARTMODE "manual" -Read .target.stat "/sys/class/net/atm5/type" nil -Read .target.string "/sys/class/net/atm5/type" nil -Dir .network.value."ci5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."ci5".BOOTPROTO "dhcp" -Read .network.value."ci5".STARTMODE "manual" -Read .target.stat "/sys/class/net/ci5/type" nil -Read .target.string "/sys/class/net/ci5/type" nil -Dir .network.value."ctc5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."ctc5".BOOTPROTO "dhcp" -Read .network.value."ctc5".STARTMODE "manual" -Read .target.stat "/sys/class/net/ctc5/type" nil -Read .target.string "/sys/class/net/ctc5/type" nil -Dir .network.value."dummy5": ["BOOTPROTO", "IPADDR", "NETMASK", "STARTMODE"] -Read .network.value."dummy5".BOOTPROTO "static" -Read .network.value."dummy5".IPADDR "1.2.3.4" -Read .network.value."dummy5".NETMASK "255.0.0.0" -Read .network.value."dummy5".STARTMODE "manual" -Read .target.stat "/sys/class/net/dummy5/type" nil -Read .target.string "/sys/class/net/dummy5/type" nil -Dir .network.value."escon5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."escon5".BOOTPROTO "dhcp" -Read .network.value."escon5".STARTMODE "manual" -Read .target.stat "/sys/class/net/escon5/type" nil -Read .target.string "/sys/class/net/escon5/type" nil -Dir .network.value."eth0.3": ["BOOTPROTO", "ETHERDEVICE", "STARTMODE"] -Read .network.value."eth0.3".BOOTPROTO "dhcp" -Read .network.value."eth0.3".ETHERDEVICE "eth0" -Read .network.value."eth0.3".STARTMODE "manual" -Read .target.stat "/sys/class/net/eth0.3/type" nil -Read .target.string "/sys/class/net/eth0.3/type" nil -Dir .network.value."eth5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."eth5".BOOTPROTO "dhcp" -Read .network.value."eth5".STARTMODE "manual" -Read .target.stat "/sys/class/net/eth5/type" nil -Read .target.string "/sys/class/net/eth5/type" nil -Dir .network.value."eth6": ["BOOTPROTO", "IPADDR", "STARTMODE"] -Read .network.value."eth6".BOOTPROTO "static" -Read .network.value."eth6".IPADDR "1.2.3.4" -Read .network.value."eth6".STARTMODE "manual" -Read .target.stat "/sys/class/net/eth6/type" nil -Read .target.string "/sys/class/net/eth6/type" nil -Dir .network.value."eth7": ["STARTMODE"] -Read .network.value."eth7".STARTMODE "manual" -Read .target.stat "/sys/class/net/eth7/type" nil -Read .target.string "/sys/class/net/eth7/type" nil -Dir .network.value."eth8": ["IPADDR", "STARTMODE"] -Read .network.value."eth8".IPADDR "1.2.3.4/8" -Read .network.value."eth8".STARTMODE "manual" -Read .target.stat "/sys/class/net/eth8/type" nil -Read .target.string "/sys/class/net/eth8/type" nil -Dir .network.value."eth9": ["IPADDR", "PREFIXLEN", "STARTMODE"] -Read .network.value."eth9".IPADDR "1.2.3.4" -Read .network.value."eth9".PREFIXLEN "8" -Read .network.value."eth9".STARTMODE "manual" -Read .target.stat "/sys/class/net/eth9/type" nil -Read .target.string "/sys/class/net/eth9/type" nil -Dir .network.value."fddi5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."fddi5".BOOTPROTO "dhcp" -Read .network.value."fddi5".STARTMODE "manual" -Read .target.stat "/sys/class/net/fddi5/type" nil -Read .target.string "/sys/class/net/fddi5/type" nil -Dir .network.value."hippi5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."hippi5".BOOTPROTO "dhcp" -Read .network.value."hippi5".STARTMODE "manual" -Read .target.stat "/sys/class/net/hippi5/type" nil -Read .target.string "/sys/class/net/hippi5/type" nil -Dir .network.value."hsi5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."hsi5".BOOTPROTO "dhcp" -Read .network.value."hsi5".STARTMODE "manual" -Read .target.stat "/sys/class/net/hsi5/type" nil -Read .target.string "/sys/class/net/hsi5/type" nil -Dir .network.value."ippp5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."ippp5".BOOTPROTO "dhcp" -Read .network.value."ippp5".STARTMODE "manual" -Read .target.stat "/sys/class/net/ippp5/type" nil -Read .target.string "/sys/class/net/ippp5/type" nil -Dir .network.value."iucv5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."iucv5".BOOTPROTO "dhcp" -Read .network.value."iucv5".STARTMODE "manual" -Read .target.stat "/sys/class/net/iucv5/type" nil -Read .target.string "/sys/class/net/iucv5/type" nil -Dir .network.value."lo": ["BROADCAST", "IPADDR", "NETMASK", "NETWORK", "STARTMODE"] -Read .network.value."lo".BROADCAST "127.255.255.255" -Read .network.value."lo".IPADDR "127.0.0.1" -Read .network.value."lo".NETMASK "255.0.0.0" -Read .network.value."lo".NETWORK "127.0.0.0" -Read .network.value."lo".STARTMODE "onboot" -Read .target.stat "/sys/class/net/lo/type" nil -Read .target.string "/sys/class/net/lo/type" nil -Dir .network.value."mynet0": ["BOOTPROTO", "INTERFACETYPE", "STARTMODE"] -Read .network.value."mynet0".BOOTPROTO "dhcp" -Read .network.value."mynet0".INTERFACETYPE "eth" -Read .network.value."mynet0".STARTMODE "auto" -Read .target.stat "/sys/class/net/mynet0/type" nil -Read .target.string "/sys/class/net/mynet0/type" nil -Dir .network.value."myri5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."myri5".BOOTPROTO "dhcp" -Read .network.value."myri5".STARTMODE "manual" -Read .target.stat "/sys/class/net/myri5/type" nil -Read .target.string "/sys/class/net/myri5/type" nil -Dir .network.value."myvlantoo": ["BOOTPROTO", "ETHERDEVICE", "STARTMODE", "VLAN_ID"] -Read .network.value."myvlantoo".BOOTPROTO "dhcp" -Read .network.value."myvlantoo".ETHERDEVICE "eth0" -Read .network.value."myvlantoo".STARTMODE "manual" -Read .network.value."myvlantoo".VLAN_ID "2" -Read .target.stat "/sys/class/net/myvlantoo/type" nil -Read .target.string "/sys/class/net/myvlantoo/type" nil -Dir .network.value."ppp5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."ppp5".BOOTPROTO "dhcp" -Read .network.value."ppp5".STARTMODE "manual" -Read .target.stat "/sys/class/net/ppp5/type" nil -Read .target.string "/sys/class/net/ppp5/type" nil -Dir .network.value."tr5": ["BOOTPROTO", "STARTMODE"] -Read .network.value."tr5".BOOTPROTO "dhcp" -Read .network.value."tr5".STARTMODE "manual" -Read .target.stat "/sys/class/net/tr5/type" nil -Read .target.string "/sys/class/net/tr5/type" nil -Dir .network.value."virtlan4": ["BOOTPROTO", "ETHERDEVICE", "STARTMODE"] -Read .network.value."virtlan4".BOOTPROTO "dhcp" -Read .network.value."virtlan4".ETHERDEVICE "eth0" -Read .network.value."virtlan4".STARTMODE "manual" -Read .target.stat "/sys/class/net/virtlan4/type" nil -Read .target.string "/sys/class/net/virtlan4/type" nil -Dir .network.value."vlan3": ["BOOTPROTO", "ETHERDEVICE", "STARTMODE"] -Read .network.value."vlan3".BOOTPROTO "dhcp" -Read .network.value."vlan3".ETHERDEVICE "eth0" -Read .network.value."vlan3".STARTMODE "manual" -Read .target.stat "/sys/class/net/vlan3/type" nil -Read .target.string "/sys/class/net/vlan3/type" nil -Return true -Dump all=$["arc":$["arc5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "atm":$["atm5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ci":$["ci5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ctc":$["ctc5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "dummy":$["dummy5":$["BOOTPROTO":"static", "IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"]], "escon":$["escon5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "eth":$["eth5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"], "eth6":$["BOOTPROTO":"static", "IPADDR":"1.2.3.4", "NETMASK":"255.255.255.255", "PREFIXLEN":"32", "STARTMODE":"manual"], "eth7":$["STARTMODE":"manual"], "eth8":$["IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"], "eth9":$["IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"]], "fddi":$["fddi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "hippi":$["hippi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "hsi":$["hsi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ippp":$["ippp5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "iucv":$["iucv5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "lo":$["lo":$["BROADCAST":"127.255.255.255", "IPADDR":"127.0.0.1", "NETMASK":"255.0.0.0", "NETWORK":"127.0.0.0", "PREFIXLEN":"8", "STARTMODE":"auto"]], "mynet":$["mynet0":$["BOOTPROTO":"dhcp", "STARTMODE":"auto"]], "myri":$["myri5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ppp":$["ppp5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "tr":$["tr5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "vlan":$["eth0.3":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"], "myvlantoo":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual", "VLAN_ID":"2"], "virtlan4":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"], "vlan3":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"]]] -Dump NetworkInterfaces::Write -Write .network.value."eth5".BOOTPROTO "dhcp" true -Write .network.value."eth5".STARTMODE "manual" true -Write .network.value."eth6".BOOTPROTO "static" true -Write .network.value."eth6".IPADDR "1.2.3.4/32" true -Write .network.value."eth6".PREFIXLEN "32" true -Write .network.value."eth6".STARTMODE "manual" true -Write .network.value."eth7".STARTMODE "manual" true -Write .network.value."eth8".IPADDR "1.2.3.4/8" true -Write .network.value."eth8".PREFIXLEN "8" true -Write .network.value."eth8".STARTMODE "manual" true -Write .network.value."eth9".IPADDR "1.2.3.4/8" true -Write .network.value."eth9".PREFIXLEN "8" true -Write .network.value."eth9".STARTMODE "manual" true -Write .network nil true -Return true -Write .network.value."ppp5".BOOTPROTO "dhcp" true -Write .network.value."ppp5".STARTMODE "manual" true -Write .network nil true -Return true -Write .network.value."ippp5".BOOTPROTO "dhcp" true -Write .network.value."ippp5".STARTMODE "manual" true -Write .network nil true -Return true -Return true -Write .network.value."atm5".BOOTPROTO "dhcp" true -Write .network.value."atm5".STARTMODE "manual" true -Write .network.value."tr5".BOOTPROTO "dhcp" true -Write .network.value."tr5".STARTMODE "manual" true -Write .network nil true -Return true -Write .network.value."arc5".BOOTPROTO "dhcp" true -Write .network.value."arc5".STARTMODE "manual" true -Write .network.value."ci5".BOOTPROTO "dhcp" true -Write .network.value."ci5".STARTMODE "manual" true -Write .network.value."ctc5".BOOTPROTO "dhcp" true -Write .network.value."ctc5".STARTMODE "manual" true -Write .network.value."dummy5".BOOTPROTO "static" true -Write .network.value."dummy5".IPADDR "1.2.3.4/8" true -Write .network.value."dummy5".PREFIXLEN "8" true -Write .network.value."dummy5".STARTMODE "manual" true -Write .network.value."escon5".BOOTPROTO "dhcp" true -Write .network.value."escon5".STARTMODE "manual" true -Write .network.value."fddi5".BOOTPROTO "dhcp" true -Write .network.value."fddi5".STARTMODE "manual" true -Write .network.value."hippi5".BOOTPROTO "dhcp" true -Write .network.value."hippi5".STARTMODE "manual" true -Write .network.value."hsi5".BOOTPROTO "dhcp" true -Write .network.value."hsi5".STARTMODE "manual" true -Write .network.value."iucv5".BOOTPROTO "dhcp" true -Write .network.value."iucv5".STARTMODE "manual" true -Write .network.value."lo".BROADCAST "127.255.255.255" true -Write .network.value."lo".IPADDR "127.0.0.1/8" true -Write .network.value."lo".NETWORK "127.0.0.0" true -Write .network.value."lo".PREFIXLEN "8" true -Write .network.value."lo".STARTMODE "auto" true -Write .network.value."mynet0".BOOTPROTO "dhcp" true -Write .network.value."mynet0".STARTMODE "auto" true -Write .network.value."myri5".BOOTPROTO "dhcp" true -Write .network.value."myri5".STARTMODE "manual" true -Write .network.value."eth0.3".BOOTPROTO "dhcp" true -Write .network.value."eth0.3".ETHERDEVICE "eth0" true -Write .network.value."eth0.3".STARTMODE "manual" true -Write .network.value."myvlantoo".BOOTPROTO "dhcp" true -Write .network.value."myvlantoo".ETHERDEVICE "eth0" true -Write .network.value."myvlantoo".STARTMODE "manual" true -Write .network.value."myvlantoo".VLAN_ID "2" true -Write .network.value."virtlan4".BOOTPROTO "dhcp" true -Write .network.value."virtlan4".ETHERDEVICE "eth0" true -Write .network.value."virtlan4".STARTMODE "manual" true -Write .network.value."vlan3".BOOTPROTO "dhcp" true -Write .network.value."vlan3".ETHERDEVICE "eth0" true -Write .network.value."vlan3".STARTMODE "manual" true -Write .network nil true -Return true -Dump NetworkInterfaces::Export -Dump exported=$["arc":$["arc5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "atm":$["atm5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ci":$["ci5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ctc":$["ctc5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "dummy":$["dummy5":$["BOOTPROTO":"static", "IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"]], "escon":$["escon5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "eth":$["eth5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"], "eth6":$["BOOTPROTO":"static", "IPADDR":"1.2.3.4", "NETMASK":"255.255.255.255", "PREFIXLEN":"32", "STARTMODE":"manual"], "eth7":$["STARTMODE":"manual"], "eth8":$["IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"], "eth9":$["IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"]], "fddi":$["fddi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "hippi":$["hippi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "hsi":$["hsi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ippp":$["ippp5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "iucv":$["iucv5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "lo":$["lo":$["BROADCAST":"127.255.255.255", "IPADDR":"127.0.0.1", "NETMASK":"255.0.0.0", "NETWORK":"127.0.0.0", "PREFIXLEN":"8", "STARTMODE":"auto"]], "mynet":$["mynet0":$["BOOTPROTO":"dhcp", "STARTMODE":"auto"]], "myri":$["myri5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ppp":$["ppp5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "tr":$["tr5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "vlan":$["eth0.3":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"], "myvlantoo":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual", "VLAN_ID":"2"], "virtlan4":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"], "vlan3":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"]]] -Dump NetworkInterfaces::Import -Dump all =$["arc":$["arc5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "atm":$["atm5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ci":$["ci5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ctc":$["ctc5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "dummy":$["dummy5":$["BOOTPROTO":"static", "IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"]], "escon":$["escon5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "eth":$["eth5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"], "eth6":$["BOOTPROTO":"static", "IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"], "eth7":$["STARTMODE":"manual"], "eth8":$["IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"], "eth9":$["IPADDR":"1.2.3.4", "NETMASK":"255.0.0.0", "PREFIXLEN":"8", "STARTMODE":"manual"]], "fddi":$["fddi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "hippi":$["hippi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "hsi":$["hsi5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ippp":$["ippp5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "iucv":$["iucv5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "lo":$["lo":$["BROADCAST":"127.255.255.255", "IPADDR":"127.0.0.1", "NETMASK":"255.0.0.0", "NETWORK":"127.0.0.0", "PREFIXLEN":"8", "STARTMODE":"auto"]], "mynet":$["mynet0":$["BOOTPROTO":"dhcp", "STARTMODE":"auto"]], "myri":$["myri5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "ppp":$["ppp5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "tr":$["tr5":$["BOOTPROTO":"dhcp", "STARTMODE":"manual"]], "vlan":$["eth0.3":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"], "myvlantoo":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual", "VLAN_ID":"2"], "virtlan4":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"], "vlan3":$["BOOTPROTO":"dhcp", "ETHERDEVICE":"eth0", "STARTMODE":"manual"]]] -Dump NetworkInterfaces::List -Return [] -Return [] -Return ["lo"] -Return [] -Return ["eth0"] -Return ["eth0", "lo"] -Return [] -Return ["eth0", "tr1"] -Return ["eth0", "tr1"] diff --git a/library/network/testsuite/tests/NetworkInterfaces2.rb b/library/network/testsuite/tests/NetworkInterfaces2.rb deleted file mode 100644 index 38622eb51..000000000 --- a/library/network/testsuite/tests/NetworkInterfaces2.rb +++ /dev/null @@ -1,210 +0,0 @@ -# encoding: utf-8 - -# *************************************************************************** -# -# Copyright (c) 2002 - 2012 Novell, Inc. -# 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 Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, -# you may find current contact information at www.novell.com -# -# *************************************************************************** -module Yast - # inject NetworkInterfaces accessor so we can modify Devices - class NetworkInterfacesClass < Module - attr_accessor :Devices - attr_accessor :OriginalDevices - end - - class NetworkInterfaces2Client < Client - def main - Yast.include self, "testsuite.rb" - - @READ = { - "network" => { - "section" => { - "arc5" => nil, - "atm5" => nil, - "ci5" => nil, - "ctc5" => nil, - "dummy5" => nil, - "escon5" => nil, - "eth5" => nil, - "eth6" => nil, - "eth7" => nil, - "eth8" => nil, - "eth9" => nil, - # "eth-pcmcia": nil, - # "eth-usb" : nil, - "mynet0" => nil, - "fddi5" => nil, - "hippi5" => nil, - "hsi5" => nil, - "ippp5" => nil, - "iucv5" => nil, - "lo" => nil, - "myri5" => nil, - "ppp5" => nil, - "tr5" => nil, - "tr~" => nil, - "vlan3" => nil, - "eth0.3" => nil, - "virtlan4" => nil, - "myvlantoo" => nil - }, - "value" => { - "arc5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "atm5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ci5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ctc5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "dummy5" => { - "BOOTPROTO" => "static", - "IPADDR" => "1.2.3.4", - "NETMASK" => "255.0.0.0", - "STARTMODE" => "manual" - }, - "escon5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "eth5" => - # "IPADDR_x":"1.1.1.1", "NETMASK_x":"0.0.0.0" - { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - # 32 bit prefix - "eth6" => { - "BOOTPROTO" => "static", - "IPADDR" => "1.2.3.4", - "STARTMODE" => "manual" - }, - "eth7" => { "STARTMODE" => "manual" }, - "eth8" => { "IPADDR" => "1.2.3.4/8", "STARTMODE" => "manual" }, - "eth9" => { - "IPADDR" => "1.2.3.4", - "PREFIXLEN" => "8", - "STARTMODE" => "manual" - }, - # "eth-pcmcia": $["BOOTPROTO":"dhcp", "STARTMODE":"hotplug"], - # "eth-usb" : $["BOOTPROTO":"dhcp", "STARTMODE":"hotplug"], - "mynet0" => { - "BOOTPROTO" => "dhcp", - "STARTMODE" => "auto", - "INTERFACETYPE" => "eth" - }, - "fddi5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "hippi5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "hsi5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ippp5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "iucv5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "lo" => - # "IPADDR_1":"7.7.7.7" - { - "BROADCAST" => "127.255.255.255", - "IPADDR" => "127.0.0.1", - "NETMASK" => "255.0.0.0", - "NETWORK" => "127.0.0.0", - "STARTMODE" => "onboot" - }, - "myri5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "ppp5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "tr5" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" }, - "vlan3" => { - "BOOTPROTO" => "dhcp", - "STARTMODE" => "manual", - "ETHERDEVICE" => "eth0" - }, - "eth0.3" => { - "BOOTPROTO" => "dhcp", - "STARTMODE" => "manual", - "ETHERDEVICE" => "eth0" - }, - "virtlan4" => { - "BOOTPROTO" => "dhcp", - "STARTMODE" => "manual", - "ETHERDEVICE" => "eth0" - }, - "myvlantoo" => { - "BOOTPROTO" => "dhcp", - "STARTMODE" => "manual", - "ETHERDEVICE" => "eth0", - "VLAN_ID" => "2" - } - } - }, - "probe" => { "system" => [] }, - "target" => { "tmpdir" => "/tmp" } - } - - @EXEC = { - "target" => { - "bash_output" => { "exit" => 0, "stdout" => "", "stderr" => "" } - } - } - - TESTSUITE_INIT([@READ, {}, @EXEC], nil) - Yast.import "NetworkInterfaces" - - DUMP("NetworkInterfaces::Read") - TEST(->() { NetworkInterfaces.Read }, [@READ, {}, @EXEC], nil) - DUMP(Builtins.sformat("all=%1", NetworkInterfaces.Devices)) - NetworkInterfaces.OriginalDevices = nil - - DUMP("NetworkInterfaces::Write") - TEST(->() { NetworkInterfaces.Write("eth") }, [@READ], nil) - TEST(->() { NetworkInterfaces.Write("ppp") }, [@READ], nil) - TEST(->() { NetworkInterfaces.Write("ippp") }, [@READ], nil) - TEST(->() { NetworkInterfaces.Write("trx") }, [@READ], nil) - TEST(->() { NetworkInterfaces.Write("atm|tr") }, [@READ], nil) - TEST(->() { NetworkInterfaces.Write("") }, [@READ], nil) - - @exported = nil - - DUMP("NetworkInterfaces::Export") - @exported = NetworkInterfaces.Export("") - DUMP(Builtins.sformat("exported=%1", @exported)) - - # Test import canonicalizing - DUMP("NetworkInterfaces::Import") - Ops.set(@exported, ["lo", "lo", "STARTMODE"], "boot") - Ops.set(@exported, ["eth", "eth6", "IPADDR"], "1.2.3.4/8") - NetworkInterfaces.Import("", @exported) - DUMP(Builtins.sformat("all =%1", NetworkInterfaces.Devices)) - - DUMP("NetworkInterfaces::List") - NetworkInterfaces.Devices = { - "lo" => { "lo" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" } } - } - TEST(->() { NetworkInterfaces.List("modem") }, [], nil) - TEST(->() { NetworkInterfaces.List("netcard") }, [], nil) - TEST(->() { NetworkInterfaces.List("") }, [], nil) - NetworkInterfaces.Devices = { - "lo" => { "lo" => { "BOOTPROTO" => "dhcp", "STARTMODE" => "manual" } }, - "eth" => { - "eth0" => { "BOOTPROTO" => "DHCP", "STARTMODE" => "manual" } - } - } - TEST(->() { NetworkInterfaces.List("modem") }, [], nil) - TEST(->() { NetworkInterfaces.List("netcard") }, [], nil) - TEST(->() { NetworkInterfaces.List("") }, [], nil) - NetworkInterfaces.Devices = { - "eth" => { "eth0" => { "BOOTPROTO" => "static" } }, - "tr" => { "tr1" => { "BOOTPROTO" => "dhcp" } } - } - TEST(->() { NetworkInterfaces.List("modem") }, [], nil) - TEST(->() { NetworkInterfaces.List("netcard") }, [], nil) - TEST(->() { NetworkInterfaces.List("") }, [], nil) - - nil - end - end -end - -Yast::NetworkInterfaces2Client.new.main From f31ddf9c3d47228211b51be71414e5fd3dcba36b Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 13:54:58 +0200 Subject: [PATCH 136/223] Updated testsuite - removed tests for dropped methods --- .../test/network_interfaces_helpers_test.rb | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/library/network/test/network_interfaces_helpers_test.rb b/library/network/test/network_interfaces_helpers_test.rb index 6f2ad89c2..2eb12c983 100755 --- a/library/network/test/network_interfaces_helpers_test.rb +++ b/library/network/test/network_interfaces_helpers_test.rb @@ -6,69 +6,6 @@ module Yast describe NetworkInterfaces do - context "Parsing device name" do - DEVICE_DESCS = [ - { - name: "", - alias_id: "", - type_by_regex: "" - }, - { - name: "eth0", - alias_id: "", - type_by_regex: "eth" - }, - { - name: "eth-pcmcia-0", - alias_id: "", - type_by_regex: "eth" - }, - { - name: "tr-pcmcia-1#0", - alias_id: "0", - type_by_regex: "tr" - }, - { - name: "enp0s3", - alias_id: "", - type_by_regex: "enp" - }, - { - name: "eth0#1", - alias_id: "1", - type_by_regex: "eth" - }, - { - name: "enp0s3#0", - alias_id: "0", - type_by_regex: "enp" - }, - { - name: "eth-id-00:07:e9:d5:8e:e8", - alias_id: "", - type_by_regex: "eth" - } - ].freeze - - DEVICE_DESCS.each do |device_desc| - device_name = device_desc[:name] - alias_id = device_desc[:alias_id] - type_by_regex = device_desc[:type_by_regex] - - describe "#alias_num" do - it "returns alias_id: <#{alias_id}> for name: <#{device_name}>" do - expect(NetworkInterfaces.alias_num(device_name)).to be_eql alias_id - end - end - - describe "#device_type" do - it "returns type by regex: <#{type_by_regex}> for name: <#{device_name}>" do - expect(NetworkInterfaces.device_type(device_name)).to be_eql type_by_regex - end - end - end - end - describe "NetworkInterfaces#filter_interfacetype" do it "drops interface type if present and not set to \"lo\" or \"dummy\"" do devmap = { "INTERFACETYPE" => "eth" } From 2ad21870ac16671ba1fa997cc38ac9a9c56789ca Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 14:00:11 +0200 Subject: [PATCH 137/223] Disabled rspec tests affected by the API change - need improvements --- library/network/test/network_interfaces_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index a5441916d..800ff277b 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -47,6 +47,7 @@ end it "loads all valid devices from ifcfg-* definition" do + skip("needs improvement due to obsolete API removal") subject.Read expect(subject.List("")).to eql devices end @@ -112,6 +113,7 @@ context "when given regex is some of the predefined ones 'netcard', 'modem', 'isdn', 'dsl'." do it "returns devices of the given type" do + skip("needs improvement due to obsolete API removal") expect(subject.FilterDevices("netcard").keys).to eql(netcard_devices) expect(subject.FilterDevices("modem").keys).to eql(["ppp"]) expect(subject.FilterDevices("dsl").keys).to eql([]) @@ -143,6 +145,7 @@ context "given a list of device types and a regex" do it "returns device types that don't match the given regex" do + skip("needs improvement due to obsolete API removal") expect(subject.FilterNOT(subject.FilterDevices(""), "eth").keys) .to eql(["arc", "bond", "br", "cold", "em", "ppp", "vlan"]) end @@ -238,6 +241,7 @@ end it "returns an array of devices which have got a different key,value than given ones" do + skip("needs improvement due to obsolete API removal") expect(subject.LocateNOT("BOOTPROTO", "static")).to eql(["arc5", "br1", "cold", "ppp0", "vlan3"]) end end From 13bfe436b9fb758c183f78520be069f7fe61b02d Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 14:13:09 +0200 Subject: [PATCH 138/223] Dropped NetworkInterfaces#device_num and #GetFreeDevices --- .../network/src/modules/NetworkInterfaces.rb | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 0cda2eb15..00366276e 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -393,18 +393,6 @@ def GetDeviceTypeName(dev) end end - # Return a device number - # @param [String] dev device - # @return device number - # @example device_num("eth1") -> "1" - # @example device_num("lo") -> "" - # - # Obsolete: It is incompatible with new device naming scheme. - def device_num(dev) - log.warn("Do not use device_num.") - ifcfg_part(dev, "2") - end - # Create a device name from its type and number # @param [String] typ device type # @param [String] num device number @@ -1280,40 +1268,6 @@ def Modified(devregex) devs == original_devs end - # It returns an array of elements corresponding to the integer - # part of the free device names available for the given device type. - # - # @example GetFreeDevices("eth", 2) -> [1, 2] - # - # @param [String] type device type - # @param [String] num number of free devices to return - # @return [Array] of free devices for given type - def GetFreeDevices(type, num) - log.debug("Devices=#{@Devices}") - log.debug("type,num=#{type},#{num}") - log.debug("Devices[#{type}]=#{@Devices[type]}") - - curdevs = @Devices.fetch(type, {}).keys - curdevs.map! { |d| d.include?(type) ? device_num(d) : d } - - i = 0 - count = 0 - ret = [] - - # Remaining numbered devices - while count < num - if !curdevs.include?(i.to_s) - ret << i.to_s - count += 1 - end - i += 1 - end - - log.debug("Free devices=#{ret}") - - ret - end - # Return free device # @param [String] type device type # @return free device @@ -1335,7 +1289,6 @@ def GetFreeDevice(type) def Check(dev) Builtins.y2debug("Check(%1)", dev) typ = GetType(dev) - # string num = device_num(dev); return false if !Builtins.haskey(@Devices, typ) devsmap = Ops.get(@Devices, typ, {}) @@ -1696,7 +1649,6 @@ def get_devices(devregex) publish function: :GetTypeFromIfcfg, type: "string (map )" publish function: :GetType, type: "string (string)" publish function: :GetDeviceTypeName, type: "string (string)" - publish function: :device_num, type: "string (string)" publish function: :IsHotplug, type: "boolean (string)" publish function: :IsConnected, type: "boolean (string)" publish function: :RealType, type: "string (string, string)" From 77d3a0c4eb553e0202c7a984e83f522cf17d12a3 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 14:15:17 +0200 Subject: [PATCH 139/223] Adapted testsuite --- library/network/test/network_interfaces_test.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 800ff277b..fb3373970 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -197,17 +197,6 @@ end end - describe "#GetFreeDevices" do - it "returns an array with available device numbers" do - subject.instance_variable_set(:@Devices, "eth" => { "0" => {} }) - expect(subject.GetFreeDevices("eth", 2)).to eql(["1", "2"]) - subject.instance_variable_set(:@Devices, "eth" => { "1" => {} }) - expect(subject.GetFreeDevices("eth", 2)).to eql(["0", "2"]) - subject.instance_variable_set(:@Devices, "eth" => { "2" => {} }) - expect(subject.GetFreeDevices("eth", 2)).to eql(["0", "1"]) - end - end - describe "#Locate" do let(:data_dir) { File.join(File.dirname(__FILE__), "data") } From e2b05b20694ab6eabd5d1af11f3ee012f70ce865 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 14:16:48 +0200 Subject: [PATCH 140/223] Dropped NetworkInterfaces#ifcfg_part --- library/network/src/modules/NetworkInterfaces.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 00366276e..7ec481fbb 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -185,12 +185,6 @@ def IsEmpty(value) value.nil? ? true : value.empty? end - def ifcfg_part(ifcfg, part) - return "" if Builtins.regexpmatch(ifcfg, @ifcfg_name_regex) != true - ret = Builtins.regexpsub(ifcfg, @ifcfg_name_regex, "\\#{part}") - ret.nil? ? "" : ret - end - # Detects a subtype of Ethernet device type according /sys or /proc content # # @example From 27b496df69b15af1f4a3002b4837ec325dd8e249 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 2 Aug 2018 14:23:31 +0200 Subject: [PATCH 141/223] Happy rubocop --- library/network/src/modules/NetworkInterfaces.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 7ec481fbb..60fb173c4 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -1371,10 +1371,6 @@ def Change2(name, newdev, check) end Builtins.y2debug("ChangeDevice(%1)", name) - devsmap = Ops.get(@Devices, t, {}) - devmap = Ops.get(devsmap, name, {}) - amap = Ops.get_map(devmap, "_aliases", {}) - devmap = deep_copy(newdev) Ops.set(devsmap, name, devmap) From 07d4ab1012d1cbe7bb00564853a64d41fe089ade Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Tue, 4 Sep 2018 11:04:40 +0200 Subject: [PATCH 142/223] Dropped unused methods --- .../network/src/modules/NetworkInterfaces.rb | 26 ------------------- .../network/test/network_interfaces_test.rb | 17 ------------ 2 files changed, 43 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 60fb173c4..8c01b8c39 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -1481,23 +1481,6 @@ def Locate(key, val) ret end - # Locate devices which attributes doesn't match given key and value - # - # @param [String] key device key - # @param [String] val device value - # @return [Array] of devices with key!=val - def LocateNOT(key, val) - ret = [] - - @Devices.values.each do |devsmap| - devsmap.each do |device, conf| - ret << device if conf[key] != val - end - end - - ret - end - # @deprecated No longer needed # @return true def CleanHotplugSymlink @@ -1587,14 +1570,6 @@ def ValidCharsIfcfg String.ValidCharsFilename end - # list of all devices except given one by parameter dev - # also loopback is ommited - - def ListDevicesExcept(dev) - devices = Builtins.filter(LocateNOT("DEVICE", dev)) { |s| s != "lo" } - deep_copy(devices) - end - private # Device configuration files are matched against this regexp @@ -1672,7 +1647,6 @@ def get_devices(devregex) publish function: :Fastest, type: "string ()" publish function: :FastestType, type: "string (string)" publish function: :ValidCharsIfcfg, type: "string ()" - publish function: :ListDevicesExcept, type: "list (string)" end NetworkInterfaces = NetworkInterfacesClass.new diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index fb3373970..fc47392c4 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -218,21 +218,4 @@ end end - describe "#Locate" do - let(:data_dir) { File.join(File.dirname(__FILE__), "data") } - - around do |example| - change_scr_root(data_dir, &example) - end - - before do - subject.CleanCacheRead - end - - it "returns an array of devices which have got a different key,value than given ones" do - skip("needs improvement due to obsolete API removal") - expect(subject.LocateNOT("BOOTPROTO", "static")).to eql(["arc5", "br1", "cold", "ppp0", "vlan3"]) - end - end - end From d293c978e905ac8ce6cc8bbf50e4288b3093bae6 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 5 Sep 2018 09:37:31 +0200 Subject: [PATCH 143/223] Adapted tests to the new API --- library/network/src/modules/NetworkInterfaces.rb | 2 +- library/network/test/network_interfaces_test.rb | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 8c01b8c39..0f60396e0 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -297,7 +297,7 @@ def GetTypeFromIfcfg(ifcfg) rule_key = Ops.get(key_type, 0, "") rule_value = Ops.get(key_type, 1, "") rule_type = Ops.get(key_type, 2, "") - type = rule_type if Ops.get_string(ifcfg, rule_key, "") == rule_value + type = rule_type if (ifcfg[rule_key] || "").downcase == rule_value.downcase end Builtins.foreach(@TypeByKeyExistence) do |key_type| diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index fc47392c4..9e5f3fd67 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -47,9 +47,8 @@ end it "loads all valid devices from ifcfg-* definition" do - skip("needs improvement due to obsolete API removal") subject.Read - expect(subject.List("")).to eql devices + expect(subject.List("").sort).to eql devices end it "doesn't load ifcfgs with a backup extension" do @@ -101,7 +100,7 @@ describe "#FilterDevices" do let(:data_dir) { File.join(File.dirname(__FILE__), "data") } # Defined in test/data/etc/sysconfig/ifcfg-* - let(:netcard_devices) { ["arc", "bond", "br", "em", "eth", "vlan"] } + let(:netcard_devices) { ["arc", "bond", "br", "eth", "vlan"] } around do |example| change_scr_root(data_dir, &example) @@ -113,7 +112,6 @@ context "when given regex is some of the predefined ones 'netcard', 'modem', 'isdn', 'dsl'." do it "returns devices of the given type" do - skip("needs improvement due to obsolete API removal") expect(subject.FilterDevices("netcard").keys).to eql(netcard_devices) expect(subject.FilterDevices("modem").keys).to eql(["ppp"]) expect(subject.FilterDevices("dsl").keys).to eql([]) @@ -209,7 +207,7 @@ end it "returns an array of devices which have got given key,value" do - expect(subject.Locate("BOOTPROTO", "static")).to eql(["bond0", "em1", "eth0", "eth1", "eth2"]) + expect(subject.Locate("BOOTPROTO", "static").sort).to eql(["bond0", "em1", "eth0", "eth1", "eth2"]) expect(subject.Locate("BONDING_MASTER", "YES")).to eql(["bond0"]) end From 0c0cb0013390022a53fe108e06d2d0bca55096e9 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 5 Sep 2018 10:32:49 +0200 Subject: [PATCH 144/223] Dropped netcard type arc 1) it stands for an obsolete ARCNet. We do not have specific support for this kind of infrastructure (similar situation to token ring) 2) currently no way how to decide if ifcfg-arc0 is configuration for ARCNet or persistent name for an eth device --- library/network/src/modules/NetworkInterfaces.rb | 3 +-- library/network/test/network_interfaces_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 0f60396e0..3590497aa 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -101,7 +101,7 @@ def main @CardRegex = # other: irlan|lo|plip|... { - "netcard" => "arc|ath|bnep|ci|ctc|slc|dummy|bond|escon|eth|fddi|ficon|hsi|qeth|lcs|iucv|myri|tr|usb|wlan|xp|vlan|br|tun|tap|ib|em|p|p[0-9]+p", + "netcard" => "ath|bnep|ci|ctc|slc|dummy|bond|escon|eth|fddi|ficon|hsi|qeth|lcs|iucv|myri|tr|usb|wlan|xp|vlan|br|tun|tap|ib|em|p|p[0-9]+p", "modem" => "ppp|modem", "isdn" => "isdn|ippp", "dsl" => "dsl" @@ -1021,7 +1021,6 @@ def GetDeviceTypes # device types which cannot be present on s390 arch s390_unknown_dev_types = [ - "arc", "bnep", "dummy", "fddi", diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 9e5f3fd67..587ab40ac 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -100,7 +100,7 @@ describe "#FilterDevices" do let(:data_dir) { File.join(File.dirname(__FILE__), "data") } # Defined in test/data/etc/sysconfig/ifcfg-* - let(:netcard_devices) { ["arc", "bond", "br", "eth", "vlan"] } + let(:netcard_devices) { ["bond", "br", "eth", "vlan"] } around do |example| change_scr_root(data_dir, &example) @@ -112,7 +112,7 @@ context "when given regex is some of the predefined ones 'netcard', 'modem', 'isdn', 'dsl'." do it "returns devices of the given type" do - expect(subject.FilterDevices("netcard").keys).to eql(netcard_devices) + expect(subject.FilterDevices("netcard").keys.sort).to eql(netcard_devices) expect(subject.FilterDevices("modem").keys).to eql(["ppp"]) expect(subject.FilterDevices("dsl").keys).to eql([]) expect(subject.FilterDevices("isdn").keys).to eql([]) From b2e65a5a9ca5b49621a8f37e7867277662e17332 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 5 Sep 2018 11:00:24 +0200 Subject: [PATCH 145/223] adapted testsuite --- .../network/src/modules/NetworkInterfaces.rb | 1 - .../network/test/network_interfaces_test.rb | 28 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 3590497aa..fcca92666 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -254,7 +254,6 @@ def GetTypeFromSysfs(dev) sys_type = Convert.to_string( SCR.Read(path(".target.string"), sys_type_path) ) - sys_type = if sys_type Builtins.regexpsub(sys_type, "(.*)\n", "\\1") else diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 587ab40ac..1c3507402 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -98,6 +98,8 @@ end describe "#FilterDevices" do + TYPE_SYS_PATH = "/sys/class/net/ppp0/type".freeze + let(:data_dir) { File.join(File.dirname(__FILE__), "data") } # Defined in test/data/etc/sysconfig/ifcfg-* let(:netcard_devices) { ["bond", "br", "eth", "vlan"] } @@ -106,11 +108,28 @@ change_scr_root(data_dir, &example) end - before do - subject.CleanCacheRead - end - context "when given regex is some of the predefined ones 'netcard', 'modem', 'isdn', 'dsl'." do + before do + # mock type id for ppp device in sysfs + allow(Yast::FileUtils) + .to receive(:Exists) + .and_return false + allow(Yast::FileUtils) + .to receive(:Exists) + .with(TYPE_SYS_PATH) + .and_return true + + allow(Yast::SCR) + .to receive(:Read) + .and_call_original + allow(Yast::SCR) + .to receive(:Read) + .with(path(".target.string"), TYPE_SYS_PATH) + .and_return "512\n" + + subject.CleanCacheRead + end + it "returns devices of the given type" do expect(subject.FilterDevices("netcard").keys.sort).to eql(netcard_devices) expect(subject.FilterDevices("modem").keys).to eql(["ppp"]) @@ -118,6 +137,7 @@ expect(subject.FilterDevices("isdn").keys).to eql([]) end end + context "when given regex is not a predefined one" do it "returns devices whose type exactly match the given regex" do expect(subject.FilterDevices("br").keys).to eql(["br"]) From 829b4ffe23faf33e11db3a1ba1e623fd9ad8875e Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Tue, 11 Sep 2018 09:18:21 +0200 Subject: [PATCH 146/223] Happy rubocop --- library/network/src/modules/NetworkInterfaces.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index fcca92666..cca658e86 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -296,7 +296,7 @@ def GetTypeFromIfcfg(ifcfg) rule_key = Ops.get(key_type, 0, "") rule_value = Ops.get(key_type, 1, "") rule_type = Ops.get(key_type, 2, "") - type = rule_type if (ifcfg[rule_key] || "").downcase == rule_value.downcase + type = rule_type if (ifcfg[rule_key] || "").casecmp?(rule_value) end Builtins.foreach(@TypeByKeyExistence) do |key_type| From a86f9b8763cbfc46491d73b953a74241f45b1913 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Tue, 11 Sep 2018 09:46:14 +0200 Subject: [PATCH 147/223] Adapted testsuite --- .../network/test/network_interfaces_test.rb | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 1c3507402..6c032ebf9 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -8,6 +8,8 @@ subject { Yast::NetworkInterfaces } + TYPE_SYS_PATH = "/sys/class/net/ppp0/type".freeze + describe "#CanonicalizeIP" do context "Handling IPv6 address" do it "Sets ipaddr, prefix and empty mask" do @@ -98,8 +100,6 @@ end describe "#FilterDevices" do - TYPE_SYS_PATH = "/sys/class/net/ppp0/type".freeze - let(:data_dir) { File.join(File.dirname(__FILE__), "data") } # Defined in test/data/etc/sysconfig/ifcfg-* let(:netcard_devices) { ["bond", "br", "eth", "vlan"] } @@ -130,7 +130,7 @@ subject.CleanCacheRead end - it "returns devices of the given type" do + it "returns device groups of the given type" do expect(subject.FilterDevices("netcard").keys.sort).to eql(netcard_devices) expect(subject.FilterDevices("modem").keys).to eql(["ppp"]) expect(subject.FilterDevices("dsl").keys).to eql([]) @@ -139,7 +139,7 @@ end context "when given regex is not a predefined one" do - it "returns devices whose type exactly match the given regex" do + it "returns device groups whose type exactly match the given regex" do expect(subject.FilterDevices("br").keys).to eql(["br"]) expect(subject.FilterDevices("br").size).to eql(1) expect(subject.FilterDevices("vlan").keys).to eql(["vlan"]) @@ -158,14 +158,30 @@ end before do + # mock type id for ppp device in sysfs + allow(Yast::FileUtils) + .to receive(:Exists) + .and_return false + allow(Yast::FileUtils) + .to receive(:Exists) + .with(TYPE_SYS_PATH) + .and_return true + + allow(Yast::SCR) + .to receive(:Read) + .and_call_original + allow(Yast::SCR) + .to receive(:Read) + .with(path(".target.string"), TYPE_SYS_PATH) + .and_return "512\n" + subject.CleanCacheRead end context "given a list of device types and a regex" do - it "returns device types that don't match the given regex" do - skip("needs improvement due to obsolete API removal") + it "returns device groups that don't match the given regex" do expect(subject.FilterNOT(subject.FilterDevices(""), "eth").keys) - .to eql(["arc", "bond", "br", "cold", "em", "ppp", "vlan"]) + .to eql(["bond", "br", "ppp", "vlan"]) end end end From 4345f5c64c21c4b347ca803ab10beb0ccc76f999 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 12 Sep 2018 08:35:52 +0200 Subject: [PATCH 148/223] Moved test for NetworkInterfaces#Export from old testsuite into rspec --- .../network/test/network_interfaces_test.rb | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 6c032ebf9..78eb5d782 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -4,6 +4,25 @@ Yast.import("NetworkInterfaces") +def mock_ppp + # mock type id for ppp device in sysfs + allow(Yast::FileUtils) + .to receive(:Exists) + .and_return false + allow(Yast::FileUtils) + .to receive(:Exists) + .with(TYPE_SYS_PATH) + .and_return true + + allow(Yast::SCR) + .to receive(:Read) + .and_call_original + allow(Yast::SCR) + .to receive(:Read) + .with(path(".target.string"), TYPE_SYS_PATH) + .and_return "512\n" +end + describe Yast::NetworkInterfaces do subject { Yast::NetworkInterfaces } @@ -110,22 +129,7 @@ context "when given regex is some of the predefined ones 'netcard', 'modem', 'isdn', 'dsl'." do before do - # mock type id for ppp device in sysfs - allow(Yast::FileUtils) - .to receive(:Exists) - .and_return false - allow(Yast::FileUtils) - .to receive(:Exists) - .with(TYPE_SYS_PATH) - .and_return true - - allow(Yast::SCR) - .to receive(:Read) - .and_call_original - allow(Yast::SCR) - .to receive(:Read) - .with(path(".target.string"), TYPE_SYS_PATH) - .and_return "512\n" + mock_ppp subject.CleanCacheRead end @@ -158,22 +162,7 @@ end before do - # mock type id for ppp device in sysfs - allow(Yast::FileUtils) - .to receive(:Exists) - .and_return false - allow(Yast::FileUtils) - .to receive(:Exists) - .with(TYPE_SYS_PATH) - .and_return true - - allow(Yast::SCR) - .to receive(:Read) - .and_call_original - allow(Yast::SCR) - .to receive(:Read) - .with(path(".target.string"), TYPE_SYS_PATH) - .and_return "512\n" + mock_ppp subject.CleanCacheRead end @@ -186,6 +175,30 @@ end end + describe "#Export" do + let(:data_dir) { File.join(File.dirname(__FILE__), "data") } + # Defined in test/data/etc/sysconfig/ifcfg-* + let(:netcard_types) { ["bond", "br", "eth", "ppp", "vlan"].sort } + + around do |example| + change_scr_root(data_dir, &example) + end + + before do + mock_ppp + + subject.CleanCacheRead + end + + it "exports all devices in a hash with device type as a key and list of devices as value" do + exported_devs = subject.Export("") + + expect(exported_devs.keys.sort).to eql netcard_types + expect(exported_devs["eth"]).to include("cold", "em1", "eth0") + expect(exported_devs["ppp"]).to include("ppp0") + end + end + describe "#ConcealSecrets1" do let(:ifcfg_out) do { From 14b6336d454fe5d3e6a32651bf4017bc6c90a83a Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 12 Sep 2018 09:32:01 +0200 Subject: [PATCH 149/223] Moved test from NetworkInterfacex#Import from old testsuite into rspec --- library/network/test/network_interfaces_test.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 78eb5d782..ce1f15227 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -199,6 +199,21 @@ def mock_ppp end end + describe "#Import" do + let(:network_devices) do + { + "eth" => { "enp0s3" => { "STARTMODE" => "auto" }}, + "ppp" => { "ppp1" => { "DEVICE" => "ppp1" }} + } + end + + it "imports all devices" do + subject.Import("", network_devices) + + expect(subject.List("")).to include("enp0s3", "ppp1") + end + end + describe "#ConcealSecrets1" do let(:ifcfg_out) do { From e30547b6b946da0dddb0a4cc9585010285b2f636 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 12 Sep 2018 09:47:15 +0200 Subject: [PATCH 150/223] Happy ruboco --- library/network/test/network_interfaces_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index ce1f15227..662f86d2b 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -202,8 +202,8 @@ def mock_ppp describe "#Import" do let(:network_devices) do { - "eth" => { "enp0s3" => { "STARTMODE" => "auto" }}, - "ppp" => { "ppp1" => { "DEVICE" => "ppp1" }} + "eth" => { "enp0s3" => { "STARTMODE" => "auto" } }, + "ppp" => { "ppp1" => { "DEVICE" => "ppp1" } } } end From db626c52ff250273059cac43784857454a5a0b7e Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 17 Sep 2018 10:50:20 +0200 Subject: [PATCH 151/223] Dropped GetFreeDevice. Functionality moved to yast2-network Bcs yast2-network has overview even about existing but not configured devices. --- library/network/src/modules/NetworkInterfaces.rb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index cca658e86..859ab4e7b 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -1260,21 +1260,6 @@ def Modified(devregex) devs == original_devs end - # Return free device - # @param [String] type device type - # @return free device - # @example GetFreeDevice("eth") -> "1" - def GetFreeDevice(type) - log.debug("type=#{type}") - - free_dev = GetFreeDevices(type, 1).first - - log.error("Free device location error: #{free_dev}") if free_dev.nil? - log.debug("Free device=#{free_dev}") - - free_dev - end - # Check presence of the device (alias) # @param [String] dev device identifier # @return true if device is present From 8932d8b82d8e81ef98dfa984362aa023d7f4894f Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 17 Sep 2018 10:56:36 +0200 Subject: [PATCH 152/223] Updated changelog --- package/yast2.changes | 9 +++++++++ package/yast2.spec | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index e83071386..06097ffa9 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Wed Sep 19 12:55:51 UTC 2018 - mfilka@suse.com + +- bnc#964856 + - removed obsolete parts of NetworkInterfaces API: + ifcfg_part, device_type, device_num, alias_num, + GetFreeDevices, GetFreeDevice, LocateNOT, ListDevicesExcept +- 4.1.16 + ------------------------------------------------------------------- Wed Sep 19 09:53:09 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index bb1a3b655..cabbd5774 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.15 +Version: 4.1.16 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 9c66fe5417181268c258650d76d0952be7c44c24 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Tue, 18 Sep 2018 09:38:00 +0200 Subject: [PATCH 153/223] Cleanup --- library/network/src/modules/NetworkInterfaces.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index 859ab4e7b..de047fa76 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -1593,7 +1593,6 @@ def get_devices(devregex) publish variable: :Name, type: "string" publish variable: :Current, type: "map " publish variable: :CardRegex, type: "map " - publish function: :device_type, type: "string (string)" publish function: :GetTypeFromIfcfg, type: "string (map )" publish function: :GetType, type: "string (string)" publish function: :GetDeviceTypeName, type: "string (string)" @@ -1611,8 +1610,6 @@ def get_devices(devregex) publish function: :GetDeviceTypes, type: "list ()" publish function: :GetDevTypeDescription, type: "string (string, boolean)" publish function: :Export, type: "map (string)" - publish function: :GetFreeDevices, type: "list (string, integer)" - publish function: :GetFreeDevice, type: "string (string)" publish function: :Check, type: "boolean (string)" publish function: :Select, type: "boolean (string)" publish function: :Add, type: "boolean ()" From 02bef2a63320983233a71a1d9219775a3b6dfdd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 19 Sep 2018 14:35:11 +0100 Subject: [PATCH 154/223] Y2Firewall::Firewalld::Interface#zone returns a Zone object --- library/network/src/lib/y2firewall/firewalld/interface.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/network/src/lib/y2firewall/firewalld/interface.rb b/library/network/src/lib/y2firewall/firewalld/interface.rb index 636cf8179..629b1798c 100644 --- a/library/network/src/lib/y2firewall/firewalld/interface.rb +++ b/library/network/src/lib/y2firewall/firewalld/interface.rb @@ -61,11 +61,9 @@ def device_name # Return the zone name for a given interface from the firewalld instance # instead of from the API. # - # @return [String, nil] zone name whether belongs to some or nil if not + # @return [Y2Firewall::Firewalld::Zone,nil] zone if it belongs to some or nil otherwise def zone - zone = fw.zones.find { |z| z.interfaces.include?(id.to_s) } - - zone ? zone.name : nil + fw.zones.find { |z| z.interfaces.include?(name) } end private From 6fa7203f247e611fe5af781a8a64ed56b9363493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 19 Sep 2018 14:35:41 +0100 Subject: [PATCH 155/223] Add Y2Firewall::Firewalld::Interface unit tests --- .../src/lib/y2firewall/firewalld/interface.rb | 5 +- .../y2firewall/firewalld/interface_test.rb | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 library/network/test/y2firewall/firewalld/interface_test.rb diff --git a/library/network/src/lib/y2firewall/firewalld/interface.rb b/library/network/src/lib/y2firewall/firewalld/interface.rb index 629b1798c..20031e2cd 100644 --- a/library/network/src/lib/y2firewall/firewalld/interface.rb +++ b/library/network/src/lib/y2firewall/firewalld/interface.rb @@ -21,6 +21,7 @@ require "yast" require "y2firewall/firewalld" +Yast.import "NetworkInterfaces" module Y2Firewall class Firewalld @@ -33,8 +34,6 @@ class Interface # # @param name [String] interface name def initialize(name) - Yast.import "NetworkInterfaces" - @id = name.to_sym end @@ -42,9 +41,7 @@ def initialize(name) # # @return [Array] known interfaces def self.known - Yast.import "NetworkInterfaces" interfaces = Yast::NetworkInterfaces.List("").reject { |i| i == "lo" } - interfaces.map { |i| new(i) } end diff --git a/library/network/test/y2firewall/firewalld/interface_test.rb b/library/network/test/y2firewall/firewalld/interface_test.rb new file mode 100644 index 000000000..a931cd34a --- /dev/null +++ b/library/network/test/y2firewall/firewalld/interface_test.rb @@ -0,0 +1,86 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2018] 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_relative "../../test_helper" +require "y2firewall/firewalld/interface" + +describe Y2Firewall::Firewalld::Interface do + subject(:iface) { described_class.new("eth0") } + + describe ".known" do + before do + allow(Yast::NetworkInterfaces).to receive(:List).with("") + .and_return(["eth0", "lo", "wlan0"]) + end + + it "returns an object for each known interface except 'lo'" do + expect(described_class.known).to contain_exactly( + an_object_having_attributes(name: "eth0"), + an_object_having_attributes(name: "wlan0") + ) + end + end + + describe "#name" do + it "returns the interface name" do + expect(iface.name).to eq("eth0") + end + end + + describe "#id" do + it "returns the name as a symbol" do + expect(iface.id).to eq(:eth0) + end + end + + describe "#device_name" do + DEVICE_NAME = "Some Device Name".freeze + + before do + allow(Yast::NetworkInterfaces).to receive(:GetValue).with("eth0", "NAME") + .and_return(DEVICE_NAME) + end + + it "returns the device name" do + expect(iface.device_name).to eq(DEVICE_NAME) + end + end + + describe "#zone" do + let(:public_zone) do + instance_double(Y2Firewall::Firewalld::Zone, interfaces: ["eth1"]) + end + + let(:dmz_zone) do + instance_double(Y2Firewall::Firewalld::Zone, interfaces: ["eth0"]) + end + + before do + allow(Y2Firewall::Firewalld.instance).to receive(:zones) + .and_return([public_zone, dmz_zone]) + end + + it "returns the zone where the interface belongs to" do + expect(iface.zone).to eq(dmz_zone) + end + end +end From 1e2986bebcace6b80187773afd829bcceca4b726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 19 Sep 2018 14:38:10 +0100 Subject: [PATCH 156/223] Bump version and update changes file --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 4d937575f..ce340aebb 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Sep 19 13:36:40 UTC 2018 - igonzalezsosa@suse.com + +- Improve Y2Firewall::Firewalld::Interface#zone to return an + Zone object (fate#324662). +- 4.0.96 + ------------------------------------------------------------------- Wed Sep 19 09:53:09 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index ae462b175..22411d00b 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.95 +Version: 4.0.96 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 3cad85a786be245aa7496ed7d697763d007ce39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 19 Sep 2018 14:52:22 +0100 Subject: [PATCH 157/223] Adapt CWMFirewallInterfaces and Interfaces helper --- library/network/src/modules/CWMFirewallInterfaces.rb | 6 +++--- library/network/test/test_helper.rb | 3 ++- library/network/test/y2firewall/helpers/interfaces_test.rb | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/network/src/modules/CWMFirewallInterfaces.rb b/library/network/src/modules/CWMFirewallInterfaces.rb index 52c0032a1..2e5785208 100644 --- a/library/network/src/modules/CWMFirewallInterfaces.rb +++ b/library/network/src/modules/CWMFirewallInterfaces.rb @@ -249,10 +249,10 @@ def StoreAllowedInterfaces(services) return if !configuration_changed zones = - known_interfaces.each_with_object([]) do |known_interface, a| + known_interfaces.each_with_object([]) do |known_interface, memo| if allowed_interfaces.include?(known_interface.name) - zone_name = known_interface.zone || firewalld.default_zone - a << zone_name + zone_name = known_interface.zone ? known_interface.zone.name : firewalld.default_zone + memo << zone_name end end diff --git a/library/network/test/test_helper.rb b/library/network/test/test_helper.rb index a62c678c2..e52b4e8b9 100644 --- a/library/network/test/test_helper.rb +++ b/library/network/test/test_helper.rb @@ -82,7 +82,8 @@ module NetworkStubs }.freeze end -def mock_firewalld_interface(id, name, zone) +def mock_firewalld_interface(id, name, zone_name) + zone = instance_double("Y2Firewall::Firewalld::Zone", name: zone_name) instance_double("Y2Firewall::Firewalld::Interface", id: id, name: id.to_s, device_name: name, zone: zone) end diff --git a/library/network/test/y2firewall/helpers/interfaces_test.rb b/library/network/test/y2firewall/helpers/interfaces_test.rb index 1b4a3611b..27527f9de 100755 --- a/library/network/test/y2firewall/helpers/interfaces_test.rb +++ b/library/network/test/y2firewall/helpers/interfaces_test.rb @@ -40,7 +40,7 @@ class DummyClass expect(known_interfaces.size).to eql(2) eth0 = known_interfaces.find { |i| i.id == :eth0 } expect(eth0.name).to eq("eth0") - expect(eth0.zone).to eq("external") + expect(eth0.zone.name).to eq("external") expect(eth0.device_name).to eq("Intel I217-LM") expect(eth0).to be_a(Y2Firewall::Firewalld::Interface) end From 96b93570b59ab7eab7d4938d8ea7b1a394f1b98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 19 Sep 2018 14:55:34 +0100 Subject: [PATCH 158/223] Shorten variable name --- library/network/src/modules/CWMFirewallInterfaces.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/network/src/modules/CWMFirewallInterfaces.rb b/library/network/src/modules/CWMFirewallInterfaces.rb index 2e5785208..32d753c06 100644 --- a/library/network/src/modules/CWMFirewallInterfaces.rb +++ b/library/network/src/modules/CWMFirewallInterfaces.rb @@ -249,9 +249,9 @@ def StoreAllowedInterfaces(services) return if !configuration_changed zones = - known_interfaces.each_with_object([]) do |known_interface, memo| - if allowed_interfaces.include?(known_interface.name) - zone_name = known_interface.zone ? known_interface.zone.name : firewalld.default_zone + known_interfaces.each_with_object([]) do |iface, memo| + if allowed_interfaces.include?(iface.name) + zone_name = iface.zone ? iface.zone.name : firewalld.default_zone memo << zone_name end end From 29ff1421c0b9fc999b532562a987b170b36a864e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 19 Sep 2018 15:23:57 +0100 Subject: [PATCH 159/223] Adapt CWMFirewallInterfaces.Selected2Opened --- library/network/src/modules/CWMFirewallInterfaces.rb | 6 +++++- library/network/test/cwm_firewall_interfaces_test.rb | 2 +- library/network/test/y2firewall/helpers/interfaces_test.rb | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/library/network/src/modules/CWMFirewallInterfaces.rb b/library/network/src/modules/CWMFirewallInterfaces.rb index 32d753c06..3fbfe3447 100644 --- a/library/network/src/modules/CWMFirewallInterfaces.rb +++ b/library/network/src/modules/CWMFirewallInterfaces.rb @@ -161,7 +161,11 @@ def UpdateFirewallStatus # @return a list of interfaces that will be opened def Selected2Opened(ifaces, _nm_ifaces_have_to_be_selected) log.info("Selected ifaces: #{ifaces}") - zone_names = ifaces.map { |i| interface_zone(i) || default_zone.name }.uniq + zone_names = ifaces.map do |name| + zone = interface_zone(name) + zone ? zone.name : default_zone.name + end + zone_names.uniq! log.info("Ifaces zone names: #{zone_names}") zone_ifaces = diff --git a/library/network/test/cwm_firewall_interfaces_test.rb b/library/network/test/cwm_firewall_interfaces_test.rb index eda49479f..3ef612e48 100755 --- a/library/network/test/cwm_firewall_interfaces_test.rb +++ b/library/network/test/cwm_firewall_interfaces_test.rb @@ -217,7 +217,7 @@ end before do - allow(subject).to receive(:interface_zone).with("eth0").and_return("public") + allow(subject).to receive(:interface_zone).with("eth0").and_return(zone) allow(firewalld).to receive(:find_zone).and_return(zone) end diff --git a/library/network/test/y2firewall/helpers/interfaces_test.rb b/library/network/test/y2firewall/helpers/interfaces_test.rb index 27527f9de..c6a2b84e4 100755 --- a/library/network/test/y2firewall/helpers/interfaces_test.rb +++ b/library/network/test/y2firewall/helpers/interfaces_test.rb @@ -26,7 +26,7 @@ class DummyClass describe "#interface_zone" do it "returns the zone name of the given interface" do - expect(subject.interface_zone("eth0")).to eql("external") + expect(subject.interface_zone("eth0")).to eql(external) end it "returns nil if the interface does not belong to any zone" do From ec3021ff0d666df597e6060577b9a1f6d16c223e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 26 Sep 2018 18:19:18 +0100 Subject: [PATCH 160/223] Do not modify the value if it is the same. --- library/network/src/lib/y2firewall/firewalld/relations.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/network/src/lib/y2firewall/firewalld/relations.rb b/library/network/src/lib/y2firewall/firewalld/relations.rb index 9d7dcd687..82780eb42 100644 --- a/library/network/src/lib/y2firewall/firewalld/relations.rb +++ b/library/network/src/lib/y2firewall/firewalld/relations.rb @@ -81,6 +81,7 @@ def has_attributes(*attributes, scope: nil, cache: false) # rubocop:disable Styl attr_reader attribute define_method "#{attribute}=" do |item| + return item if public_send(attribute) == item instance_variable_set("@#{attribute}", item) modified!(attribute) if cache @@ -183,6 +184,7 @@ def has_many(*relations, scope: nil, cache: false) # rubocop:disable Style/Predi attr_reader relation define_method "#{relation}=" do |item| + return item if public_send(relation) == item instance_variable_set("@#{relation}", item) modified!(relation) if cache From bcc3380968d8cc402c5cc584d94d2861f9daade2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Fri, 28 Sep 2018 09:07:27 +0100 Subject: [PATCH 161/223] Bump version & changelog. --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index ce340aebb..06bca092e 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Sep 28 08:05:15 UTC 2018 - knut.anderssen@suse.com + +- Y2Firewall::Firewalld: Single attributes setter will not modify + the value of the attribute in case it is the same (bsc#1109812) +- 4.0.97 + ------------------------------------------------------------------- Wed Sep 19 13:36:40 UTC 2018 - igonzalezsosa@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 22411d00b..cd0d85644 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.96 +Version: 4.0.97 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 5eac45b14794271219fcd678edd4c25fa3bb00b1 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 27 Sep 2018 09:35:29 +0200 Subject: [PATCH 162/223] Smoke test for NetworkInterfaces::Change2 --- library/network/test/network_interfaces_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 662f86d2b..9763d574b 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -280,4 +280,9 @@ def mock_ppp end end + describe "Change2" do + it "passes smoke test" do + expect { subject.Change2("eth0", {}, false) }.not_to raise_error + end + end end From 7cfdb29b318348a2e994d223c7b7ffcab2509ac0 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 27 Sep 2018 09:18:52 +0200 Subject: [PATCH 163/223] Fixed internal error in Changes2. Caused by patch for bnc#964856 --- library/network/src/modules/NetworkInterfaces.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/network/src/modules/NetworkInterfaces.rb b/library/network/src/modules/NetworkInterfaces.rb index de047fa76..28b845eb0 100644 --- a/library/network/src/modules/NetworkInterfaces.rb +++ b/library/network/src/modules/NetworkInterfaces.rb @@ -1354,10 +1354,8 @@ def Change2(name, newdev, check) end Builtins.y2debug("ChangeDevice(%1)", name) - devmap = deep_copy(newdev) - - Ops.set(devsmap, name, devmap) - Ops.set(@Devices, t, devsmap) + # newdev is already a deep_copy created above + @Devices[t] = @Devices.fetch(t, {}).merge(name => newdev) Builtins.y2debug("Devices=%1", @Devices) true From 21d4f4ab121b2043dc10180499d557e13ed9fee3 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 27 Sep 2018 09:48:04 +0200 Subject: [PATCH 164/223] Updated changelog --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 0a6c280b9..26aa2d4c3 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Mon Oct 1 17:29:17 UTC 2018 - mfilka@suse.com + +- bnc#964856 + - fixed internal error - do not crash when updating device config +- 4.1.19 + ------------------------------------------------------------------- Fri Sep 28 08:05:15 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 369bacece..ee390507e 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.18 +Version: 4.1.19 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From a8dfa2e5be45c5ad13920ae1247e15d09c7387b8 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 1 Oct 2018 19:11:43 +0200 Subject: [PATCH 165/223] Updated testsuite --- .../network/test/network_interfaces_test.rb | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/library/network/test/network_interfaces_test.rb b/library/network/test/network_interfaces_test.rb index 9763d574b..86f858577 100755 --- a/library/network/test/network_interfaces_test.rb +++ b/library/network/test/network_interfaces_test.rb @@ -281,8 +281,29 @@ def mock_ppp end describe "Change2" do + let(:device) { "eth0" } + let(:device_type) { "eth" } + let(:device_map) { { "BOOTPROTO" => "dhcp" } } + + before(:each) do + allow(subject).to receive(:GetType).and_return(device_type) + end + it "passes smoke test" do - expect { subject.Change2("eth0", {}, false) }.not_to raise_error + expect { subject.Change2(device, {}, false) }.not_to raise_error + end + + it "successfully merges devices of unknown type" do + expect(subject.Change2(device, device_map, false)).to be true + expect(subject.Devices).to have_key(device_type) + expect(subject.Devices[device_type]).to include(device => device_map) + end + + it "successfully adds device to existing type group" do + expect(subject.Change2(device, device_map, false)).to be true + expect(subject.Devices).to have_key(device_type) + expect(subject.Change2("eth1", {}, false)).to be true + expect(subject.Devices[device_type]).to include(device => device_map, "eth1" => {}) end end end From ce3ea94d2c3a28ae0150c1d5ce651847407e570e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 2 Oct 2018 13:17:32 +0200 Subject: [PATCH 166/223] Make the service status label stretchable (bsc#1110407) Ensure the updated status is displayed correctly - 4.1.20 --- library/systemd/src/lib/yast2/service_widget.rb | 4 ++-- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/library/systemd/src/lib/yast2/service_widget.rb b/library/systemd/src/lib/yast2/service_widget.rb index 04e42069a..400de75e4 100644 --- a/library/systemd/src/lib/yast2/service_widget.rb +++ b/library/systemd/src/lib/yast2/service_widget.rb @@ -82,8 +82,8 @@ def content Left( HBox( Label(_("Current status:")), - Label(" "), - Label(Id(:service_widget_status), status) + HSpacing(1), + Label(Id(:service_widget_status), Opt(:hstretch), status) ) ), Left(action_widget), diff --git a/package/yast2.changes b/package/yast2.changes index 26aa2d4c3..757e34725 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Oct 2 11:11:26 UTC 2018 - lslezak@suse.cz + +- Make the service status label stretchable so the updated status + is displayed correctly (bsc#1110407) +- 4.1.20 + ------------------------------------------------------------------- Mon Oct 1 17:29:17 UTC 2018 - mfilka@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index ee390507e..a4b11c2df 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.19 +Version: 4.1.20 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 8b11f4c2530c786922141801d74172cd5f504198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Tue, 2 Oct 2018 09:53:46 +0100 Subject: [PATCH 167/223] Added changes needed for autoyast configuration support --- .../network/src/lib/y2firewall/firewalld.rb | 41 +++++++++++--- .../src/lib/y2firewall/firewalld/interface.rb | 45 +++++++++++++--- .../src/lib/y2firewall/firewalld/zone.rb | 10 ++-- .../src/lib/y2firewall/helpers/interfaces.rb | 8 +++ .../y2firewall/firewalld/interface_test.rb | 54 ++++++++++++++++--- .../test/y2firewall/firewalld/zone_test.rb | 6 +++ .../network/test/y2firewall/firewalld_test.rb | 4 +- 7 files changed, 139 insertions(+), 29 deletions(-) diff --git a/library/network/src/lib/y2firewall/firewalld.rb b/library/network/src/lib/y2firewall/firewalld.rb index ee9b1c2d5..6acaeadd4 100644 --- a/library/network/src/lib/y2firewall/firewalld.rb +++ b/library/network/src/lib/y2firewall/firewalld.rb @@ -68,29 +68,37 @@ class Firewalld PACKAGE = "firewalld".freeze SERVICE = "firewalld".freeze + DEFAULT_ZONE = "public".freeze + DEFAULT_LOG = "off".freeze def_delegators :api, :enable!, :disable!, :reload, :running? has_attributes :log_denied_packets, :default_zone, cache: true # Constructor def initialize - @current_zone_names = [] - @current_service_names = [] - @zones = [] - @services = [] + load_defaults + untouched! @read = false end # Read the current firewalld configuration initializing the zones and other # attributes as logging. # + # @note when a minimal read is requested it does not parse the zones + # definition and also does not initialize any single value attributes + # + # @param minimal [Boolean] when true does a minimal object initialization # @return [Boolean] true - def read + def read(minimal: false) return false unless installed? - @zones = zone_reader.read @current_zone_names = api.zones @current_service_names = api.services - read_attributes + if minimal + @zones = current_zone_names.map { |n| Zone.new(name: n) } + else + @zones = zone_reader.read + read_attributes + end # The list of services is not read or initialized because takes time and # affects to the performance and also the services are rarely touched. @read = true @@ -238,13 +246,30 @@ def system_service @system_service ||= Yast2::SystemService.find(SERVICE) end + # Reset all the changes done initializing the instance with the defaults + def reset + load_defaults + untouched! + @read = false + end + private + # Modifies the instance with default values + def load_defaults + @current_zone_names = [] + @current_service_names = [] + @zones = [] + @services = [] + @log_denied_packets = DEFAULT_LOG + @default_zone = DEFAULT_ZONE + end + # Convenience method to instantiate a new zone reader # # @return [ZoneReader] def zone_reader - ZoneReader.new(api.zones, api.list_all_zones(verbose: true)) + ZoneReader.new(current_zone_names, api.list_all_zones(verbose: true)) end end end diff --git a/library/network/src/lib/y2firewall/firewalld/interface.rb b/library/network/src/lib/y2firewall/firewalld/interface.rb index 20031e2cd..18fa50f98 100644 --- a/library/network/src/lib/y2firewall/firewalld/interface.rb +++ b/library/network/src/lib/y2firewall/firewalld/interface.rb @@ -27,6 +27,7 @@ module Y2Firewall class Firewalld # Class to work with firewalld interfaces class Interface + include Yast::I18n # @return [Symbol] attr_accessor :id @@ -34,15 +35,43 @@ class Interface # # @param name [String] interface name def initialize(name) + textdomain "firewall" @id = name.to_sym end - # Return an array with all the known or configured interfaces + class << self + # Return an instance of Y2Firewall::Firewalld + # + # @return [Y2Firewall::Firewalld] a firewalld instance + def fw + Y2Firewall::Firewalld.instance + end + + # Return an array with all the known or configured interfaces + # + # @return [Array] known interfaces + def known + interfaces = Yast::NetworkInterfaces.List("").reject { |i| i == "lo" } + interfaces.map { |i| new(i) } + end + + # Return an array with all the interfaces that belongs to some firewalld + # zone but are not known (sysconfig configured) + # + # @return [Array] known interfaces + def unknown + known_interfaces = Yast::NetworkInterfaces.List("").reject { |i| i == "lo" } + configured_interfaces = fw.zones.map(&:interfaces).flatten.compact + (configured_interfaces - known_interfaces).map { |i| new(i) } + end + end + + # Return whether the zone is a known one # - # @return [Array] known interfaces - def self.known - interfaces = Yast::NetworkInterfaces.List("").reject { |i| i == "lo" } - interfaces.map { |i| new(i) } + # @see .known + # @return [Boolean] true if the interface is known + def known? + Yast::NetworkInterfaces.List("").reject { |i| i == "lo" }.include?(name) end # @return [String] interface name @@ -50,8 +79,12 @@ def name id.to_s end - # @return [String] device name + # Return the network interface device name in case it is configured or + # 'Unknown' in other case + # + # @return [String] its device name or 'Unknown' if not configured def device_name + return _("Unknown") unless known? Yast::NetworkInterfaces.GetValue(name, "NAME") end diff --git a/library/network/src/lib/y2firewall/firewalld/zone.rb b/library/network/src/lib/y2firewall/firewalld/zone.rb index 307892765..30e934fd4 100644 --- a/library/network/src/lib/y2firewall/firewalld/zone.rb +++ b/library/network/src/lib/y2firewall/firewalld/zone.rb @@ -50,10 +50,7 @@ class Zone has_many :services, :interfaces, :protocols, :ports, :sources, cache: true # @see Y2Firewall::Firewalld::Relations - has_attributes :name, :short, :description, :target, cache: true - - # @return [Boolean] Whether masquerade is enabled or not - attr_reader :masquerade + has_attributes :name, :masquerade, :short, :description, :target, cache: true alias_method :masquerade?, :masquerade @@ -65,6 +62,7 @@ class Zone # @param name [String] zone name def initialize(name: nil) @name = name || api.default_zone + relations.each { |r| public_send("#{r}=", []) } end def self.known_zones @@ -132,8 +130,8 @@ def service_open?(service) # @return [Hash] zone configuration def export (attributes + relations) - .each_with_object("masquerade" => masquerade) do |field, profile| - profile[field.to_s] = public_send(field) + .each_with_object({}) do |field, profile| + profile[field.to_s] = public_send(field) unless public_send(field).nil? end end diff --git a/library/network/src/lib/y2firewall/helpers/interfaces.rb b/library/network/src/lib/y2firewall/helpers/interfaces.rb index 09277d0f8..39e3c9f9f 100644 --- a/library/network/src/lib/y2firewall/helpers/interfaces.rb +++ b/library/network/src/lib/y2firewall/helpers/interfaces.rb @@ -72,6 +72,14 @@ def known_interfaces @known_interfaces = Y2Firewall::Firewalld::Interface.known end + + # Return an array with all the interfaces configured in some firewalld + # zone but not configured in sysconfig. + # + # @return [Array] unknown interfaces + def unknown_interfaces + Y2Firewall::Firewalld::Interface.unknown + end end end end diff --git a/library/network/test/y2firewall/firewalld/interface_test.rb b/library/network/test/y2firewall/firewalld/interface_test.rb index a931cd34a..ce3f53b3e 100644 --- a/library/network/test/y2firewall/firewalld/interface_test.rb +++ b/library/network/test/y2firewall/firewalld/interface_test.rb @@ -25,13 +25,14 @@ describe Y2Firewall::Firewalld::Interface do subject(:iface) { described_class.new("eth0") } + subject(:unknown_iface) { described_class.new("virbr0") } - describe ".known" do - before do - allow(Yast::NetworkInterfaces).to receive(:List).with("") - .and_return(["eth0", "lo", "wlan0"]) - end + before do + allow(Yast::NetworkInterfaces).to receive(:List).with("") + .and_return(["eth0", "lo", "wlan0"]) + end + describe ".known" do it "returns an object for each known interface except 'lo'" do expect(described_class.known).to contain_exactly( an_object_having_attributes(name: "eth0"), @@ -40,6 +41,23 @@ end end + describe ".unknown" do + let(:public_zone) do + instance_double(Y2Firewall::Firewalld::Zone, interfaces: ["eth0", "eth1", "wlan0"]) + end + + before do + allow(Y2Firewall::Firewalld.instance).to receive(:zones) + .and_return([public_zone]) + end + + it "returns an object for each unknown interface enabled in some firewalld zone" do + expect(described_class.unknown).to contain_exactly( + an_object_having_attributes(name: "eth1") + ) + end + end + describe "#name" do it "returns the interface name" do expect(iface.name).to eq("eth0") @@ -60,8 +78,16 @@ .and_return(DEVICE_NAME) end - it "returns the device name" do - expect(iface.device_name).to eq(DEVICE_NAME) + context "when the interface is known" do + it "returns the device name" do + expect(iface.device_name).to eq(DEVICE_NAME) + end + end + + context "when the interface is not known" do + it "returns the translated 'Unknown' string" do + expect(unknown_iface.device_name).to eq("Unknown") + end end end @@ -83,4 +109,18 @@ expect(iface.zone).to eq(dmz_zone) end end + + describe "#known?" do + context "when the interface is known" do + it "returns true" do + expect(iface.known?).to eql(true) + end + end + + context "when the interface is unknown" do + it "returns false" do + expect(unknown_iface.known?).to eql(false) + end + end + end end diff --git a/library/network/test/y2firewall/firewalld/zone_test.rb b/library/network/test/y2firewall/firewalld/zone_test.rb index 972c274a9..fd82ee777 100755 --- a/library/network/test/y2firewall/firewalld/zone_test.rb +++ b/library/network/test/y2firewall/firewalld/zone_test.rb @@ -79,6 +79,7 @@ context "when the zone was not modified since read" do it "returns false" do + subject.read expect(subject.modified?).to eq(false) end end @@ -175,6 +176,11 @@ end context "when the zone has been modified" do + before do + allow(subject).to receive(:apply_relations_changes!) + allow(subject).to receive(:apply_attributes_changes!) + end + subject { described_class.new(name: "test") } it "applies all the changes done in its relations" do diff --git a/library/network/test/y2firewall/firewalld_test.rb b/library/network/test/y2firewall/firewalld_test.rb index 7a9a58472..d7baca109 100755 --- a/library/network/test/y2firewall/firewalld_test.rb +++ b/library/network/test/y2firewall/firewalld_test.rb @@ -357,11 +357,11 @@ end it "applies in firewalld all the changes done in the object since read" do - firewalld.log_denied_packets = "off" + firewalld.log_denied_packets = "unicast" firewalld.default_zone = "drop" expect(api).to receive(:modify_default_zone).with("drop") - expect(api).to receive(:modify_log_denied_packets).with("off") + expect(api).to receive(:modify_log_denied_packets).with("unicast") firewalld.write_only end From 5489915d647b29c4e9cbee3f63266748b3172c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 3 Oct 2018 08:50:34 +0100 Subject: [PATCH 168/223] Bump version & changelog. --- package/yast2.changes | 8 ++++++++ package/yast2.spec | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 06bca092e..6a3cbb763 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Wed Oct 3 07:48:30 UTC 2018 - knut.anderssen@suse.com + +- Network (Firewall): + - Added some methods needed for AutoYaST configuration + (fate#324662) +- 4.0.98 + ------------------------------------------------------------------- Fri Sep 28 08:05:15 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index cd0d85644..80c27f517 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.97 +Version: 4.0.98 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 64ce75b516ebf5df46d174cb259eb0af8e8ed392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 3 Oct 2018 14:52:57 +0100 Subject: [PATCH 169/223] Adapted note based on feedback. --- library/network/src/lib/y2firewall/firewalld.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/network/src/lib/y2firewall/firewalld.rb b/library/network/src/lib/y2firewall/firewalld.rb index 6acaeadd4..a1635aebd 100644 --- a/library/network/src/lib/y2firewall/firewalld.rb +++ b/library/network/src/lib/y2firewall/firewalld.rb @@ -84,8 +84,8 @@ def initialize # Read the current firewalld configuration initializing the zones and other # attributes as logging. # - # @note when a minimal read is requested it does not parse the zones - # definition and also does not initialize any single value attributes + # @note when a minimal read is requested it neither parses the zones + # definition nor initializes any single value attributes # # @param minimal [Boolean] when true does a minimal object initialization # @return [Boolean] true From ec8c6c0b34840e5484368ce107bd0252cbfe3358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 9 Oct 2018 10:35:19 +0200 Subject: [PATCH 170/223] Log viewer: replace invalid UTF-8 characters (bsc#1110549) from the displayed log to avoid a crash - 4.0.99 --- library/system/src/clients/view_anymsg.rb | 2 ++ package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/library/system/src/clients/view_anymsg.rb b/library/system/src/clients/view_anymsg.rb index 0686f7b75..88378d652 100644 --- a/library/system/src/clients/view_anymsg.rb +++ b/library/system/src/clients/view_anymsg.rb @@ -192,6 +192,8 @@ def main file_content = SCR.Read(path(".target.string"), @filename) if file_content + # replace invalid byte sequences with Unicode "replacement character" + file_content.scrub!("�") # remove ANSI color escape sequences file_content.remove_ansi_sequences # remove remaining ASCII control characters (ASCII 0-31 and 127 (DEL)) diff --git a/package/yast2.changes b/package/yast2.changes index 6a3cbb763..54a9b3c25 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Oct 9 08:25:42 UTC 2018 - lslezak@suse.cz + +- Log viewer: replace invalid UTF-8 characters from the displayed + log to avoid a crash (bsc#1110549) +- 4.0.99 + ------------------------------------------------------------------- Wed Oct 3 07:48:30 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 80c27f517..0ba3f971c 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.98 +Version: 4.0.99 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 87c0f2c28559178d144eb9de9db8930c9cbd7139 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 10 Oct 2018 10:59:34 +0200 Subject: [PATCH 171/223] Sle 15 ga bsc 1108942 (#845) * added new calls for switching dhcp server to new firewalld interface --- .../network/src/modules/firewalld_wrapper.rb | 74 ++++++++++++++++ .../network/test/firewalld_wrapper_test.rb | 87 +++++++++++++++++++ package/yast2.changes | 7 ++ package/yast2.spec | 2 +- 4 files changed, 169 insertions(+), 1 deletion(-) diff --git a/library/network/src/modules/firewalld_wrapper.rb b/library/network/src/modules/firewalld_wrapper.rb index 999f321df..6878e84d2 100644 --- a/library/network/src/modules/firewalld_wrapper.rb +++ b/library/network/src/modules/firewalld_wrapper.rb @@ -24,6 +24,7 @@ require "yast" require "y2firewall/firewalld" +require "y2firewall/firewalld/interface" module Yast # This module add support for handling firewalld configuration and it is @@ -101,11 +102,84 @@ def remove_port(port_or_range, protocol, interface) zone.remove_port(port) end + # Check whether the firewalld service is enable or not + # + # @return [Boolean] true if it is enable; false otherwise + def is_enabled + firewalld.enabled? + end + + # Return true if the logging config or any of the zones where modified + # since read + # + # @return [Boolean] true if the config was modified; false otherwise + def is_modified + firewalld.modified? + end + + # Evaluate the zone name of an interface + # + # @param interface [String] interface name + # + # @return [String] zone name (nil; not found) + def zone_name_of_interface(interface) + zone = interface_zone(interface) + return nil unless zone + zone.name + end + + # Check if the service belongs to the zone + # + # @param service [String] service name + # @param zone [String] zone name + # + # @return [Boolean] true if service is in zone + def is_service_in_zone(service, zone_name) + zone = firewalld.find_zone(zone_name) + return false unless zone + zone.services.include?(service) + end + + # Return an array with all the known (sysconfig configured) firewalld + # interfaces. + # + # @return [Array] known interfaces + # e.g. [{ "id":"eth0", "name":"Askey 815C", "zone":"EXT"} , ... ] + def all_known_interfaces + Y2Firewall::Firewalld::Interface.known.map do |interface| + { "id" => interface.name, "zone" => zone_name_of_interface(interface.name), + "name" => interface.device_name } + end + end + + # sets status for several services on several network interfaces. + # + # @param list service ids + # @param list network interfaces + # @param boolean new status of services + def modify_interface_services(services, interfaces, status) + interfaces.each do |interface| + zone = interface_zone(interface) + next unless zone + if status + services.each { |service| zone.add_service(service) } + else + services.each { |service| zone.remove_service(service) } + end + end + end + publish function: :read, type: "boolean ()" publish function: :write, type: "boolean ()" publish function: :write_only, type: "boolean ()" publish function: :add_port, type: "boolean (string, string, string)" publish function: :remove_port, type: "boolean (string, string, string)" + publish function: :is_enabled, type: "boolean ()" + publish function: :is_modified, type: "boolean ()" + publish function: :zone_name_of_interface, type: "string (string)" + publish function: :is_service_in_zone, type: "boolean (string,string)" + publish function: :all_known_interfaces, type: "list > ()" + publish function: :modify_interface_services, type: "void (list, list, boolean)" private diff --git a/library/network/test/firewalld_wrapper_test.rb b/library/network/test/firewalld_wrapper_test.rb index 2d990c23a..5a2c9028e 100755 --- a/library/network/test/firewalld_wrapper_test.rb +++ b/library/network/test/firewalld_wrapper_test.rb @@ -1,12 +1,14 @@ #!/usr/bin/env rspec require_relative "test_helper" +require "y2firewall/firewalld/interface" Yast.import "FirewalldWrapper" describe Yast::FirewalldWrapper do let(:firewalld) { Y2Firewall::Firewalld.instance } let(:external) { Y2Firewall::Firewalld::Zone.new(name: "external") } + let(:interface) { Y2Firewall::Firewalld::Interface.new("eth0") } let(:zones) { [external] } before do @@ -14,6 +16,7 @@ allow(firewalld).to receive(:zones).and_return(zones) allow(firewalld).to receive(:installed?).and_return(true) external.interfaces = ["eth0"] + external.services = ["dhcp"] end describe "#read" do @@ -32,6 +35,22 @@ end end + describe "#is_enabled" do + it "calls firewalld.enabled?" do + expect(firewalld).to receive(:enabled?) + + subject.is_enabled + end + end + + describe "#is_modified" do + it "calls firewalld.modified?" do + expect(firewalld).to receive(:modified?) + + subject.is_modified + end + end + describe "#add_port" do before do allow(external).to receive(:add_port).and_return(true) @@ -98,4 +117,72 @@ end end end + + describe "#zone_name_of_interface" do + context "interface cannot be found" do + it "returns nil" do + expect(subject.zone_name_of_interface("wrong_interface")).to eq(nil) + end + end + + context "interface is available" do + it "returns interface zone name" do + expect(subject.zone_name_of_interface("eth0")).to eq(external.name) + end + end + end + + describe "#is_service_in_zone" do + context "zone cannot be found" do + it "returns false" do + allow(firewalld).to receive(:find_zone).and_return(nil) + expect(subject.is_service_in_zone("service", "wrong_zone")).to eq(false) + end + end + + context "zone is available" do + it "returns false if service cannot be found" do + allow(firewalld).to receive(:zones).and_return(zones) + expect(subject.is_service_in_zone("wrong_service", "wrong_zone")).to eq(false) + end + + it "returns true if service can be found" do + allow(firewalld).to receive(:zones).and_return(zones) + expect(subject.is_service_in_zone(external.services.first, "wrong_zone")).to eq(false) + end + end + end + + describe "#all_known_interfaces" do + context "interfaces are available" do + it "returns all interfaces" do + expect(Y2Firewall::Firewalld::Interface).to receive(:known).and_return([interface]) + expect(subject.all_known_interfaces).to eq([{ "id" => "eth0", "zone" => "external", "name" => "Unknown" }]) + end + end + end + + describe "#modify_interface_services" do + context "interface has no zone" do + it "do not set services" do + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:add_service) + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:remove_service) + subject.modify_interface_services(["service:dhcp-server"], ["wrong_interface"], true) + end + end + + context "interface has a zone" do + it "set services" do + expect_any_instance_of(Y2Firewall::Firewalld::Zone).to receive(:add_service) + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:remove_service) + subject.modify_interface_services(["service:dhcp-server"], ["eth0"], true) + end + + it "unset services" do + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:add_service) + expect_any_instance_of(Y2Firewall::Firewalld::Zone).to receive(:remove_service) + subject.modify_interface_services(["service:dhcp-server"], ["eth0"], false) + end + end + end end diff --git a/package/yast2.changes b/package/yast2.changes index 54a9b3c25..fb72a29e0 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Oct 9 13:57:18 CEST 2018 - schubi@suse.de + +- Added new methods to firewalld_wrapper in order to switch + yast2-dhcp-server to new firewall module. (bsc#1108942) +- 4.0.100 + ------------------------------------------------------------------- Tue Oct 9 08:25:42 UTC 2018 - lslezak@suse.cz diff --git a/package/yast2.spec b/package/yast2.spec index 0ba3f971c..02655487d 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.99 +Version: 4.0.100 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 00c75332627fb4fbe61dadfaff874bc7065cfc28 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 10 Oct 2018 12:53:33 +0200 Subject: [PATCH 172/223] Sle 15 ga bsc 1108942 (#845) (#847) * added new calls for switching dhcp server to new firewalld interface --- .../network/src/modules/firewalld_wrapper.rb | 74 ++++++++++++++++ .../network/test/firewalld_wrapper_test.rb | 87 +++++++++++++++++++ package/yast2.changes | 7 ++ package/yast2.spec | 2 +- 4 files changed, 169 insertions(+), 1 deletion(-) diff --git a/library/network/src/modules/firewalld_wrapper.rb b/library/network/src/modules/firewalld_wrapper.rb index 999f321df..6878e84d2 100644 --- a/library/network/src/modules/firewalld_wrapper.rb +++ b/library/network/src/modules/firewalld_wrapper.rb @@ -24,6 +24,7 @@ require "yast" require "y2firewall/firewalld" +require "y2firewall/firewalld/interface" module Yast # This module add support for handling firewalld configuration and it is @@ -101,11 +102,84 @@ def remove_port(port_or_range, protocol, interface) zone.remove_port(port) end + # Check whether the firewalld service is enable or not + # + # @return [Boolean] true if it is enable; false otherwise + def is_enabled + firewalld.enabled? + end + + # Return true if the logging config or any of the zones where modified + # since read + # + # @return [Boolean] true if the config was modified; false otherwise + def is_modified + firewalld.modified? + end + + # Evaluate the zone name of an interface + # + # @param interface [String] interface name + # + # @return [String] zone name (nil; not found) + def zone_name_of_interface(interface) + zone = interface_zone(interface) + return nil unless zone + zone.name + end + + # Check if the service belongs to the zone + # + # @param service [String] service name + # @param zone [String] zone name + # + # @return [Boolean] true if service is in zone + def is_service_in_zone(service, zone_name) + zone = firewalld.find_zone(zone_name) + return false unless zone + zone.services.include?(service) + end + + # Return an array with all the known (sysconfig configured) firewalld + # interfaces. + # + # @return [Array] known interfaces + # e.g. [{ "id":"eth0", "name":"Askey 815C", "zone":"EXT"} , ... ] + def all_known_interfaces + Y2Firewall::Firewalld::Interface.known.map do |interface| + { "id" => interface.name, "zone" => zone_name_of_interface(interface.name), + "name" => interface.device_name } + end + end + + # sets status for several services on several network interfaces. + # + # @param list service ids + # @param list network interfaces + # @param boolean new status of services + def modify_interface_services(services, interfaces, status) + interfaces.each do |interface| + zone = interface_zone(interface) + next unless zone + if status + services.each { |service| zone.add_service(service) } + else + services.each { |service| zone.remove_service(service) } + end + end + end + publish function: :read, type: "boolean ()" publish function: :write, type: "boolean ()" publish function: :write_only, type: "boolean ()" publish function: :add_port, type: "boolean (string, string, string)" publish function: :remove_port, type: "boolean (string, string, string)" + publish function: :is_enabled, type: "boolean ()" + publish function: :is_modified, type: "boolean ()" + publish function: :zone_name_of_interface, type: "string (string)" + publish function: :is_service_in_zone, type: "boolean (string,string)" + publish function: :all_known_interfaces, type: "list > ()" + publish function: :modify_interface_services, type: "void (list, list, boolean)" private diff --git a/library/network/test/firewalld_wrapper_test.rb b/library/network/test/firewalld_wrapper_test.rb index 2d990c23a..5a2c9028e 100755 --- a/library/network/test/firewalld_wrapper_test.rb +++ b/library/network/test/firewalld_wrapper_test.rb @@ -1,12 +1,14 @@ #!/usr/bin/env rspec require_relative "test_helper" +require "y2firewall/firewalld/interface" Yast.import "FirewalldWrapper" describe Yast::FirewalldWrapper do let(:firewalld) { Y2Firewall::Firewalld.instance } let(:external) { Y2Firewall::Firewalld::Zone.new(name: "external") } + let(:interface) { Y2Firewall::Firewalld::Interface.new("eth0") } let(:zones) { [external] } before do @@ -14,6 +16,7 @@ allow(firewalld).to receive(:zones).and_return(zones) allow(firewalld).to receive(:installed?).and_return(true) external.interfaces = ["eth0"] + external.services = ["dhcp"] end describe "#read" do @@ -32,6 +35,22 @@ end end + describe "#is_enabled" do + it "calls firewalld.enabled?" do + expect(firewalld).to receive(:enabled?) + + subject.is_enabled + end + end + + describe "#is_modified" do + it "calls firewalld.modified?" do + expect(firewalld).to receive(:modified?) + + subject.is_modified + end + end + describe "#add_port" do before do allow(external).to receive(:add_port).and_return(true) @@ -98,4 +117,72 @@ end end end + + describe "#zone_name_of_interface" do + context "interface cannot be found" do + it "returns nil" do + expect(subject.zone_name_of_interface("wrong_interface")).to eq(nil) + end + end + + context "interface is available" do + it "returns interface zone name" do + expect(subject.zone_name_of_interface("eth0")).to eq(external.name) + end + end + end + + describe "#is_service_in_zone" do + context "zone cannot be found" do + it "returns false" do + allow(firewalld).to receive(:find_zone).and_return(nil) + expect(subject.is_service_in_zone("service", "wrong_zone")).to eq(false) + end + end + + context "zone is available" do + it "returns false if service cannot be found" do + allow(firewalld).to receive(:zones).and_return(zones) + expect(subject.is_service_in_zone("wrong_service", "wrong_zone")).to eq(false) + end + + it "returns true if service can be found" do + allow(firewalld).to receive(:zones).and_return(zones) + expect(subject.is_service_in_zone(external.services.first, "wrong_zone")).to eq(false) + end + end + end + + describe "#all_known_interfaces" do + context "interfaces are available" do + it "returns all interfaces" do + expect(Y2Firewall::Firewalld::Interface).to receive(:known).and_return([interface]) + expect(subject.all_known_interfaces).to eq([{ "id" => "eth0", "zone" => "external", "name" => "Unknown" }]) + end + end + end + + describe "#modify_interface_services" do + context "interface has no zone" do + it "do not set services" do + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:add_service) + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:remove_service) + subject.modify_interface_services(["service:dhcp-server"], ["wrong_interface"], true) + end + end + + context "interface has a zone" do + it "set services" do + expect_any_instance_of(Y2Firewall::Firewalld::Zone).to receive(:add_service) + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:remove_service) + subject.modify_interface_services(["service:dhcp-server"], ["eth0"], true) + end + + it "unset services" do + expect_any_instance_of(Y2Firewall::Firewalld::Zone).not_to receive(:add_service) + expect_any_instance_of(Y2Firewall::Firewalld::Zone).to receive(:remove_service) + subject.modify_interface_services(["service:dhcp-server"], ["eth0"], false) + end + end + end end diff --git a/package/yast2.changes b/package/yast2.changes index 24a033d0b..93808561e 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Oct 9 13:57:18 CEST 2018 - schubi@suse.de + +- Added new methods to firewalld_wrapper in order to switch + yast2-dhcp-server to new firewall module. (bsc#1108942) +- 4.1.22 + ------------------------------------------------------------------- Tue Oct 9 08:25:42 UTC 2018 - lslezak@suse.cz diff --git a/package/yast2.spec b/package/yast2.spec index a8420dfc3..c20f96610 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.21 +Version: 4.1.22 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 4a600f4e1b97b6c1b9935e10fa24fab6c3431f23 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 12 Oct 2018 13:56:45 +0200 Subject: [PATCH 173/223] Added tags full_system_media_name and full_system_download_url in control.xml (#848) --- library/control/src/modules/ProductFeatures.rb | 4 +++- package/yast2.changes | 10 ++++++++++ package/yast2.spec | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/library/control/src/modules/ProductFeatures.rb b/library/control/src/modules/ProductFeatures.rb index 6b711ffab..7b08a1e83 100644 --- a/library/control/src/modules/ProductFeatures.rb +++ b/library/control/src/modules/ProductFeatures.rb @@ -70,7 +70,9 @@ def main "enable_clone" => false, "disable_os_prober" => false, # FATE #304865 - "base_product_license_directory" => "/etc/YaST2/licenses/base/" + "base_product_license_directory" => "/etc/YaST2/licenses/base/", + "full_system_media_name" => "", + "full_system_download_url" => "" }, "partitioning" => { "use_flexible_partitioning" => false, diff --git a/package/yast2.changes b/package/yast2.changes index fb72a29e0..7d243343b 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,13 @@ +------------------------------------------------------------------- +Fri Oct 12 10:28:48 CEST 2018 - schubi@suse.de + +- Added tags full_system_media_name and full_system_download_url + in control.xml which describe the location for the + "all-packages" medium. This information will be shown if the + registration has been scipped by the user. No hint will be shown + if these tags have not been defined. (fate#325834) +- 4.0.101 + ------------------------------------------------------------------- Tue Oct 9 13:57:18 CEST 2018 - schubi@suse.de diff --git a/package/yast2.spec b/package/yast2.spec index 02655487d..5a554e83e 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.100 +Version: 4.0.101 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 85a45f082a3ddef7ba96cfb819cb3cf7dc56cf86 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 12 Oct 2018 16:05:38 +0200 Subject: [PATCH 174/223] Master registration popup (#849) * Added tags full_system_media_name and full_system_download_url in control.xml (#848) --- library/control/src/modules/ProductFeatures.rb | 4 +++- package/yast2.changes | 10 ++++++++++ package/yast2.spec | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/library/control/src/modules/ProductFeatures.rb b/library/control/src/modules/ProductFeatures.rb index 6b711ffab..7b08a1e83 100644 --- a/library/control/src/modules/ProductFeatures.rb +++ b/library/control/src/modules/ProductFeatures.rb @@ -70,7 +70,9 @@ def main "enable_clone" => false, "disable_os_prober" => false, # FATE #304865 - "base_product_license_directory" => "/etc/YaST2/licenses/base/" + "base_product_license_directory" => "/etc/YaST2/licenses/base/", + "full_system_media_name" => "", + "full_system_download_url" => "" }, "partitioning" => { "use_flexible_partitioning" => false, diff --git a/package/yast2.changes b/package/yast2.changes index 93808561e..0a15be533 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,13 @@ +------------------------------------------------------------------- +Fri Oct 12 10:28:48 CEST 2018 - schubi@suse.de + +- Added tags full_system_media_name and full_system_download_url + in control.xml which describe the location for the + "all-packages" medium. This information will be shown if the + registration has been scipped by the user. No hint will be shown + if these tags have not been defined. (fate#325834) +- 4.1.23 + ------------------------------------------------------------------- Tue Oct 9 13:57:18 CEST 2018 - schubi@suse.de diff --git a/package/yast2.spec b/package/yast2.spec index c20f96610..4bc7309f1 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.22 +Version: 4.1.23 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 197f5ea30e7f4eda3fed3390d7b7e4af1a740411 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Mon, 15 Oct 2018 16:57:12 +0200 Subject: [PATCH 175/223] Yast2 package split (#850) * splitting yast2 package into yast2, yast2-logs --- package/yast2.changes | 7 +++++++ package/yast2.spec | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/package/yast2.changes b/package/yast2.changes index 0a15be533..1c1ccadae 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Mon Oct 15 16:40:32 CEST 2018 - schubi@suse.de + +- Splitting yast2 package into yast2 and yast2-logs. yast2-logs + contains only scripts for handling YaST logs (fate#325737). +- 4.1.24 + ------------------------------------------------------------------- Fri Oct 12 10:28:48 CEST 2018 - schubi@suse.de diff --git a/package/yast2.spec b/package/yast2.spec index 4bc7309f1..a711693da 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.23 +Version: 4.1.24 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only @@ -87,6 +87,8 @@ Requires: yast2-xml # new UI::SetApplicationTitle Requires: yast2-ycp-ui-bindings >= 3.2.0 Requires: yui_backend +# scripts for collecting YAST logs +Requires: yast2-logs # pre-requires for filling the sysconfig template (sysconfig.yast2) PreReq: %fillup_prereq # xdg-su in .desktops @@ -191,8 +193,8 @@ mkdir -p %{buildroot}%{_sysconfdir}/YaST2 %{_mandir}/*/* %doc %{yast_vardir}/hooks/README.md -/sbin/* -%{_sbindir}/* +/sbin/yast* +%{_sbindir}/yast* # wizard %dir %{yast_yncludedir}/wizard @@ -207,4 +209,18 @@ mkdir -p %{buildroot}%{_sysconfdir}/YaST2 %{yast_yncludedir}/hwinfo/*.rb %{yast_desktopdir}/messages.desktop +%package logs + +Summary: Collecting YAST information +Group: System/YaST + +Provides: yast2:/usr/sbin/save_y2logs + +%description logs +This package contains scripts for handling YAST logs. + +%files logs +%defattr(-,root,root) +/usr/sbin/save_y2logs + %changelog From 46a5c30fdb76436cb9d02d730533a75779371aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Tue, 16 Oct 2018 15:37:01 +0100 Subject: [PATCH 176/223] Check if a service is supported from the list of current ones --- library/network/src/modules/CWMFirewallInterfaces.rb | 8 +++----- library/network/test/cwm_firewall_interfaces_test.rb | 8 ++------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/library/network/src/modules/CWMFirewallInterfaces.rb b/library/network/src/modules/CWMFirewallInterfaces.rb index 3fbfe3447..c016ce427 100644 --- a/library/network/src/modules/CWMFirewallInterfaces.rb +++ b/library/network/src/modules/CWMFirewallInterfaces.rb @@ -211,8 +211,6 @@ def DisplayFirewallDetailsPopupHandler(widget) def InitAllowedInterfaces(services) service_status = {} - services.each { |s| firewalld.find_service(s) } - zone_services(services).each do |_s, status| status.each do |iface, en| service_status[iface] = service_status.fetch(iface, true) && en @@ -833,7 +831,7 @@ def CreateOpenFirewallWidget(settings) return { "widget" => :custom, "custom_widget" => not_installed_widget, "help" => "" } end - if services.any? { |s| !firewalld.api.service_supported?(s) } + if services.any? { |s| !firewalld.current_service_names.include?(s) } return { "widget" => :custom, "custom_widget" => services_not_defined_widget(services), @@ -1009,7 +1007,7 @@ def zone_services(services) services_status = {} services.each do |service| - service_supported = firewalld.api.service_supported?(service) + service_supported = firewalld.current_service_names.include?(service) services_status[service] = {} firewalld.zones.each do |zone| @@ -1058,7 +1056,7 @@ def interface_label(name) def services_not_defined_widget(services) services_list = services.map do |service| - if !firewalld.api.service_supported?(service) + if !firewalld.current_service_names.include?(service) # TRANSLATORS: do not modify '%{service}', it will be replaced with service name. # TRANSLATORS: item in a list, '-' is used as marker. Feel free to change it HBox(HSpacing(2), Left(Label(_("- %{service} (Not available)") % { service: service }))) diff --git a/library/network/test/cwm_firewall_interfaces_test.rb b/library/network/test/cwm_firewall_interfaces_test.rb index 3ef612e48..68bd26d87 100755 --- a/library/network/test/cwm_firewall_interfaces_test.rb +++ b/library/network/test/cwm_firewall_interfaces_test.rb @@ -11,10 +11,11 @@ subject { Yast::CWMFirewallInterfaces } let(:firewalld) { Y2Firewall::Firewalld.instance } let(:api) { instance_double("Y2Firewall::Firewalld::Api") } + let(:supported_services) { [] } before do - allow(api).to receive(:service_supported?) allow(firewalld).to receive(:api).and_return(api) + allow(firewalld).to receive(:current_service_names).and_return(supported_services) end describe "#CreateOpenFirewallWidget" do @@ -56,10 +57,6 @@ context "when the widget settings does not contain any service" do let(:widget_settings) { { "services" => ["service"] } } - before do - allow(api).to receive(:service_supported?).with("service").and_return(false) - end - it "returns a hash with only the 'widget', 'custom_widget' and 'help' keys" do allow(subject).to receive(:services_not_defined_widget).with(["service"]) .and_return(Frame("unsupported_services_summary")) @@ -72,7 +69,6 @@ it "returns a summary with the unavailable services as the 'custom_widget'" do expect(subject).to receive(:services_not_defined_widget).with(["service"]) .and_return(Frame("unsupported_services_summary")) - expect(api).to receive(:service_supported?).with("service").and_return(false) ret = subject.CreateOpenFirewallWidget(widget_settings) From 158bac49fe3b9ed4a6d9b0c3381613ec25d07587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 17 Oct 2018 16:19:50 +0100 Subject: [PATCH 177/223] Bump version & changelog. --- package/yast2.changes | 8 ++++++++ package/yast2.spec | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 7d243343b..fadb268b1 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Wed Oct 17 15:17:04 UTC 2018 - knut.anderssen@suse.com + +- CWMFirewallInterfaces: Improved the user UX replacing the api + calls for checking supported services once the list supported + ones are already known by the firewalld instance (fate#324662) +- 4.0.102 + ------------------------------------------------------------------- Fri Oct 12 10:28:48 CEST 2018 - schubi@suse.de diff --git a/package/yast2.spec b/package/yast2.spec index 5a554e83e..c241f8bc6 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.101 +Version: 4.0.102 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From c152e64a737065915f4455a2171a8b6434e9c5cd Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Tue, 9 Oct 2018 13:06:03 +0200 Subject: [PATCH 178/223] Tests for CWMClass; for removing unnecessary deep_copy's Converted old CWM tests to RSpec --- library/cwm/test/cwm_test.rb | 229 +++++++++++++++++++++++++++++ library/cwm/testsuite/tests/t1.err | 0 library/cwm/testsuite/tests/t1.out | 35 ----- library/cwm/testsuite/tests/t1.rb | 117 --------------- 4 files changed, 229 insertions(+), 152 deletions(-) create mode 100755 library/cwm/test/cwm_test.rb delete mode 100644 library/cwm/testsuite/tests/t1.err delete mode 100644 library/cwm/testsuite/tests/t1.out delete mode 100644 library/cwm/testsuite/tests/t1.rb diff --git a/library/cwm/test/cwm_test.rb b/library/cwm/test/cwm_test.rb new file mode 100755 index 000000000..7eafab339 --- /dev/null +++ b/library/cwm/test/cwm_test.rb @@ -0,0 +1,229 @@ +#! /usr/bin/env rspec --format doc + +require_relative "test_helper" + +require "yast" +Yast.import "CWM" + +# The handler methods must exist because fun_ref needs that +# but they are stubbed out. +module MyHandlers + def w1_init(_key) + raise + end + + def w1_handle(_key, _event) + raise + end + + def w1_validate(_key, _event) + raise + end + + def w2_store(_key, _event) + raise + end + + def w2_handle(_key, _event) + raise + end + + def w2_validate(_key, _event) + raise + end + + def generic_init(_key) + raise + end + + def generic_save(_key, _event) + raise + end +end + +describe Yast::CWMClass do + subject { Yast::CWM } + include Yast + include MyHandlers + + let(:test_stringterm) { VBox(HBox("w1")) } + let(:widget_names) { ["w1", "w2"] } + let(:test_widgets) do + { + "w1" => { + "widget" => :checkbox, + "opt" => [:notify, :immediate], + "label" => "Check&Box", + "init" => fun_ref(method(:w1_init), "void (string)"), + "handle" => fun_ref( + method(:w1_handle), + "symbol (string, map)" + ), + "validate_type" => :function, + "validate_function" => fun_ref( + method(:w1_validate), + "boolean (string, map)" + ) + }, + "w2" => { + "widget" => :textentry, + "label" => "Text&Entry", + "store" => fun_ref( + method(:w2_store), + "void (string, map)" + ), + "handle" => fun_ref( + method(:w2_handle), + "symbol (string, map)" + ), + "validate_type" => :function, + "validate_function" => fun_ref( + method(:w2_validate), + "boolean (string, map)" + ) + } + } + end + + let(:fallback_funcs) do + { + "init" => fun_ref(method(:generic_init), "void (string)"), + "store" => fun_ref(method(:generic_save), "void (string, map)") + } + end + + let(:created_widgets) { subject.CreateWidgets(widget_names, test_widgets) } + let(:run_widgets) { subject.mergeFunctions(created_widgets, fallback_funcs) } + + # many public uses + describe "#CreateWidgets" do + let(:cw) { created_widgets } + + it "produces an Array" do + expect(cw).to be_an(Array) + end + + it "creates Terms at the 'widget' keys" do + expect(cw[0]["widget"]).to eq(CheckBox(Id("w1"), Opt(:notify, :immediate), "Check&Box")) + expect(cw[1]["widget"]).to eq(InputField(Id("w2"), Opt(:hstretch), "Text&Entry")) + end + + # used by other CWM modules+classes + it "creates the '_cwm_key' keys" do + expect(cw[0]["_cwm_key"]).to eq("w1") + expect(cw[1]["_cwm_key"]).to eq("w2") + end + end + + # used by CWMTab + describe "#mergeFunctions" do + it "uses the second argument as fallback" do + expect(run_widgets[0]["init"].remote_method).to eq(method(:w1_init)) + expect(run_widgets[0]["store"].remote_method).to eq(method(:generic_save)) + expect(run_widgets[1]["init"].remote_method).to eq(method(:generic_init)) + expect(run_widgets[1]["store"].remote_method).to eq(method(:w2_store)) + end + end + + # used by CWMTab and CWM::ReplacePoint + describe "#initWidgets" do + it "calls init methods" do + expect(self).to receive(:w1_init).with("w1") + expect(self).to receive(:generic_init).with("w2") + subject.initWidgets(run_widgets) + end + + # used via GetProcessedWidget by yast2-slp-server and yast2 + xit "sets @processed_widget" do + end + + xit "sets ValidChars" do + end + end + + # used by CWMTab and CWM::ReplacePoint + describe "#saveWidgets" do + it "calls store methods" do + expect(self).to receive(:generic_save).with("w1", "ID" => :event) + expect(self).to receive(:w2_store).with("w2", "ID" => :event) + subject.saveWidgets(run_widgets, "ID" => :event) + end + + # used via GetProcessedWidget by yast2-slp-server and yast2 + xit "sets @processed_widget" do + end + end + + # used by CWMTab and CWM::ReplacePoint + describe "#handleWidgets" do + it "calls the handle methods" do + expect(self).to receive(:w1_handle).with("w1", "ID" => :event).and_return(nil) + expect(self).to receive(:w2_handle).with("w2", "ID" => :event).and_return(nil) + expect(subject.handleWidgets(run_widgets, "ID" => :event)).to eq(nil) + end + + it "breaks the loop if a handler returns non-nil" do + expect(self).to receive(:w1_handle).with("w1", "ID" => :event).and_return(:foo) + expect(self).to_not receive(:w2_handle) + expect(subject.handleWidgets(run_widgets, "ID" => :event)).to eq(:foo) + end + + xit "sets @processed_widget" do + end + + xit "filters the events if 'handle_events' is specified" do + end + end + + describe "#validateWidgets" do + it "calls the validate methods" do + expect(self).to receive(:w1_validate).with("w1", "ID" => :event).and_return(true) + expect(self).to receive(:w2_validate).with("w2", "ID" => :event).and_return(true) + expect(subject.validateWidgets(run_widgets, "ID" => :event)).to eq(true) + end + + it "breaks the loop if a handler returns false" do + expect(self).to receive(:w1_validate).with("w1", "ID" => :event).and_return(false) + expect(self).to_not receive(:w2_validate) + expect(subject.validateWidgets(run_widgets, "ID" => :event)).to eq(false) + end + + # SetValidationFailedHandler + xit "calls validation_failed_handler if..." do + end + end + + describe "#PrepareDialog" do + it "returns early if the term is empty" do + expect(subject).to_not receive(:ProcessTerm) + expect(subject.PrepareDialog(VBox(), test_widgets)).to eq(VBox()) + end + + it "prepares args for ProcessTerm" do + expect(subject).to receive(:ProcessTerm).with(test_stringterm, Hash) + subject.PrepareDialog(test_stringterm, created_widgets) + end + end + + # tested via its adapter PrepareDialog + describe "#ProcessTerm" do + it "replaces string ids with UI terms" do + ret = subject.PrepareDialog(test_stringterm, created_widgets) + w1 = ret.params[0].params[0] # inside VBox, HBox + expect(w1).to eq(CheckBox(Id("w1"), Opt(:notify, :immediate), "Check&Box")) + end + + xit "recurses into container widgets" do + end + + it "leaves Frame titles alone" do + ret = subject.PrepareDialog(Frame("Config", test_stringterm), created_widgets) + expect(ret.params[0]).to eq("Config") + end + + it "leaves id'd Frame titles alone" do + ret = subject.PrepareDialog(Frame(Id("f1"), "Config", test_stringterm), created_widgets) + expect(ret.params[1]).to eq("Config") + end + end +end diff --git a/library/cwm/testsuite/tests/t1.err b/library/cwm/testsuite/tests/t1.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/library/cwm/testsuite/tests/t1.out b/library/cwm/testsuite/tests/t1.out deleted file mode 100644 index 4de72f02d..000000000 --- a/library/cwm/testsuite/tests/t1.out +++ /dev/null @@ -1,35 +0,0 @@ -Dump ======================================== -Dump ========== Common stuff ============ -Dump ======================================== -Dump W1: $["_cwm_key":"w1", "custom_widget":nil, "handle":)>, "init":, "label":"Check&Box", "opt":[`notify, `immediate], "validate_function":)>, "validate_type":`function, "widget":`CheckBox (`id ("w1"), `opt (`notify, `immediate), "Check&Box")] -Dump W2: $["_cwm_key":"w2", "custom_widget":nil, "handle":)>, "label":"Text&Entry", "store":)>, "validate_function":)>, "validate_type":`function, "widget":`InputField (`id ("w2"), `opt (`hstretch), "Text&Entry")] -Dump ======================================== -Dump Merge functions -Dump Merged W1: $["_cwm_key":"w1", "custom_widget":nil, "handle":)>, "init":, "label":"Check&Box", "opt":[`notify, `immediate], "store":)>, "validate_function":)>, "validate_type":`function, "widget":`CheckBox (`id ("w1"), `opt (`notify, `immediate), "Check&Box")] -Dump Merged W2: $["_cwm_key":"w2", "custom_widget":nil, "handle":)>, "init":, "label":"Text&Entry", "store":)>, "validate_function":)>, "validate_type":`function, "widget":`InputField (`id ("w2"), `opt (`hstretch), "Text&Entry")] -Dump ========================================= -Dump Init -Log w1_init: Initing w1 -Log generic_init: Initing w2 -Dump ========================================= -Dump Handle -Dump - Both will run -Log w1_handle: Handling w1, event $["ID":`event] -Log w2_handle: Handling w2, event $["ID":`event] -Dump Returned nil -Dump - First causes event loop finish -Log w1_handle_symbol: Handling w1, event $["ID":`event] -Dump Returned `symbol -Dump ========================================= -Dump Validate -Dump - Run both -Log w1_validate: Validating w1, event $["ID":`event] -Log w2_validate: Validating w2, event $["ID":`event] -Dump Returned true -Dump - First fails -Log w1_validate_false: Validating w1, event $["ID":`event] -Dump Returned false -Dump ========================================= -Dump Save -Log generic_save: Saving w1, event $["ID":`event] -Log w2_store: Saving w2, event $["ID":`event] diff --git a/library/cwm/testsuite/tests/t1.rb b/library/cwm/testsuite/tests/t1.rb deleted file mode 100644 index 6e2837c6d..000000000 --- a/library/cwm/testsuite/tests/t1.rb +++ /dev/null @@ -1,117 +0,0 @@ -# encoding: utf-8 - -# *************************************************************************** -# -# Copyright (c) 2002 - 2012 Novell, Inc. -# 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 Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, -# you may find current contact information at www.novell.com -# -# *************************************************************************** -# File: -# t1.ycp -# -# Module: -# Common Widget Manipulation -# -# Summary: -# Common Widget Manipulation tests -# -# Authors: -# Jiri Srain -# -# $Id$ -# -# testedfiles: CWM.ycp testfunc.yh Testsuite.ycp -module Yast - class T1Client < Client - def main - Yast.include self, "testsuite.rb" - Yast.import "CWM" - Yast.import "Report" - - # disable use of UI - Report.Import("errors" => { "show" => false, "log" => true }) - - Yast.include self, "testfunc.rb" - - @functions = { - "init" => fun_ref(method(:generic_init), "void (string)"), - "store" => fun_ref(method(:generic_save), "void (string, map)") - } - - @widget_names = ["w1", "w2"] - - @ret = nil - - DUMP("========================================") - DUMP("========== Common stuff ============") - DUMP("========================================") - - @widget_data = CWM.CreateWidgets(@widget_names, @widgets) - DUMP(Builtins.sformat("W1: %1", Ops.get(@widget_data, 0))) - DUMP(Builtins.sformat("W2: %1", Ops.get(@widget_data, 1))) - - DUMP("========================================") - DUMP("Merge functions") - @widget_data = CWM.mergeFunctions(@widget_data, @functions) - DUMP(Builtins.sformat("Merged W1: %1", Ops.get(@widget_data, 0))) - DUMP(Builtins.sformat("Merged W2: %1", Ops.get(@widget_data, 1))) - - DUMP("=========================================") - DUMP("Init") - - CWM.initWidgets(@widget_data) - - DUMP("=========================================") - DUMP("Handle") - - DUMP("- Both will run") - @ret = CWM.handleWidgets(@widget_data, "ID" => :event) - DUMP(Builtins.sformat("Returned %1", @ret)) - DUMP("- First causes event loop finish") - Ops.set( - @widget_data, - [0, "handle"], - fun_ref(method(:w1_handle_symbol), "symbol (string, map)") - ) - @ret = CWM.handleWidgets(@widget_data, "ID" => :event) - DUMP(Builtins.sformat("Returned %1", @ret)) - - DUMP("=========================================") - DUMP("Validate") - DUMP("- Run both") - @ret = CWM.validateWidgets(@widget_data, "ID" => :event) - DUMP(Builtins.sformat("Returned %1", @ret)) - DUMP("- First fails") - Ops.set( - @widget_data, - [0, "validate_function"], - fun_ref(method(:w1_validat_false), "boolean (string, map)") - ) - @ret = CWM.validateWidgets(@widget_data, "ID" => :event) - DUMP(Builtins.sformat("Returned %1", @ret)) - - DUMP("=========================================") - DUMP("Save") - CWM.saveWidgets(@widget_data, "ID" => :event) - - nil - end - end -end - -Yast::T1Client.new.main From afca154c5f3bb6af916494aed6d40f69772096dc Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Wed, 10 Oct 2018 16:00:00 +0200 Subject: [PATCH 179/223] FIXME: revert: measure time spent in certain methods --- library/cwm/src/lib/cwm/replace_point.rb | 2 ++ library/cwm/src/modules/CWM.rb | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/library/cwm/src/lib/cwm/replace_point.rb b/library/cwm/src/lib/cwm/replace_point.rb index 232428223..d8cf76845 100644 --- a/library/cwm/src/lib/cwm/replace_point.rb +++ b/library/cwm/src/lib/cwm/replace_point.rb @@ -36,6 +36,7 @@ def init # handled. # @param widget [CWM::AbstractWidget] widget to display and process events def replace(widget) + Yast::Builtins.y2milestone("REPLACE BEG %1", t1 = Time.now.to_f) widgets = Yast::CWM.widgets_in_contents([widget]) @widgets_hash = widgets.map { |w| Yast::CWM.prepareWidget(w.cwm_definition) } # VBox as CWM ignore top level term and process string inside it, @@ -45,6 +46,7 @@ def replace(widget) Yast::CWM.initWidgets(@widgets_hash) @widget = widget refresh_help + Yast::Builtins.y2milestone("REPLACE END %1; %2", t2 = Time.now.to_f, t2 - t1) end # Passes to replace point content diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index 21ac4597f..996427cbb 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -714,6 +714,7 @@ def validateWidgets(widgets, event) # @param [Hash ] of maps representing widgets def CreateWidgets(names, source) + Builtins.y2milestone("CreaWid BEG %1", t1 = Time.now.to_f) names = deep_copy(names) source = deep_copy(source) ValidateMaps(source) # FIXME: find better place @@ -726,7 +727,9 @@ def CreateWidgets(names, source) deep_copy(m) end ret = Builtins.maplist(ret) { |w| prepareWidget(w) } - deep_copy(ret) + r = deep_copy(ret) + Builtins.y2milestone("CreaWid END %1; %2", t2 = Time.now.to_f, t2 - t1) + r end # Merge helps from the widgets @@ -749,6 +752,7 @@ def MergeHelps(widgets) # @param widgets [Array<::CWM::WidgetHash>] list of widget description maps # @return [::CWM::UITerm] updated term ready to be used as a dialog def PrepareDialog(dialog, widgets) + Builtins.y2milestone("PrepDia BEG %1", t1 = Time.now.to_f) dialog = deep_copy(dialog) widgets = deep_copy(widgets) args = Builtins.size(dialog) @@ -757,7 +761,9 @@ def PrepareDialog(dialog, widgets) widget_key = Ops.get_string(w, "_cwm_key", "") { widget_key => w } end - ProcessTerm(dialog, m) + t = ProcessTerm(dialog, m) + Builtins.y2milestone("PrepDia END %1; %2", t2 = Time.now.to_f, t2 - t1) + t end # Replace help for a particular widget From 495ca428dba292c2f978078429b2189bda01046a Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Wed, 10 Oct 2018 16:01:14 +0200 Subject: [PATCH 180/223] Remove redundant deep_copy in validation methods These methods could take their argument as a read-only reference - they do not modify their argument - they only pass the argument to similar methods - they do not return their argument --- library/cwm/src/modules/CWM.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index 996427cbb..cbe0fc990 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -190,7 +190,6 @@ def StringsOfTerm(t) # @param [String] type string type information # @return [Boolean] true on success or if do not know how to validate def ValidateBasicType(value, type) - value = deep_copy(value) return Ops.is_term?(value) if type == "term" return Ops.is_string?(value) if type == "string" return Ops.is_symbol?(value) if type == "symbol" @@ -210,7 +209,6 @@ def ValidateBasicType(value, type) # @param [String] widget any name of the widget/option # @return [Boolean] true if validation succeeded def ValidateValueType(key, value, widget) - value = deep_copy(value) types = { # general "widget" => "symbol", @@ -268,7 +266,6 @@ def ValidateValueType(key, value, widget) # @param [String] widget any name of the widget/option # @return [Boolean] true if validation succeeded def ValidateValueContents(key, value, widget) - value = deep_copy(value) error = "" if key == "label" s = Convert.to_string(value) @@ -291,8 +288,9 @@ def ValidateValueContents(key, value, widget) false end + # @param widgets [Array<::CWM::WidgetHash>] list of widget maps + # @return [Integer] the lowest 'ui_timeout' value, or 0 def GetLowestTimeout(widgets) - widgets = deep_copy(widgets) minimum = 0 Builtins.foreach(widgets) do |w| timeout = Ops.get_integer(w, "ui_timeout", 0) @@ -868,9 +866,9 @@ def Run(widgets, functions, skip_store_for: []) end # Disable given bottom buttons of the wizard sequencer - # @param buttons list of buttons to be disabled + # @param buttons [Array] list of buttons to be disabled: + # "back_button", "abort_button", "next_button" def DisableButtons(buttons) - buttons = deep_copy(buttons) Builtins.foreach(buttons) do |button| Wizard.DisableBackButton if button == "back_button" Wizard.DisableAbortButton if button == "abort_button" @@ -913,7 +911,6 @@ def AdjustButtons(next_, back, abort, _help) # Set handler to be called after validation of a dialog failed # @param [void ()] handler a function reference to be caled. If nil, nothing is called def SetValidationFailedHandler(handler) - handler = deep_copy(handler) @validation_failed_handler = deep_copy(handler) nil From 13211e016b6bb0ade2bc8bb060fe840eab88f3d2 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Thu, 11 Oct 2018 15:13:46 +0200 Subject: [PATCH 181/223] Optimized PrepareDialog and ProcessTerm only one deep_copy is left, just in case the caller modifies the return value --- library/cwm/src/modules/CWM.rb | 126 +++++++++++++++------------------ library/cwm/test/cwm_test.rb | 3 + 2 files changed, 59 insertions(+), 70 deletions(-) diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index cbe0fc990..7ae1d3836 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -55,36 +55,36 @@ def main # Handler to be called after validation of a dialog fails @validation_failed_handler = nil - - # UI containers, layout helpers that contain other widgets. Used by - # functions that recurse through "contents" to decide whether to go - # deeper. - @ContainerWidgets = [ - :Frame, - :RadioButtonGroup, - :VBox, - :HBox, - :MarginBox, - :MinWidth, - :MinHeight, - :MinSize, - :Left, - :Right, - :Top, - :Bottom, - :HCenter, - :VCenter, - :HVCenter, - :HSquash, - :VSquash, - :HVSquash, - :HWeight, - :VWeight, - :DumbTab, - :ReplacePoint - ] end + # UI containers, layout helpers that contain other widgets. Used by + # functions that recurse through "contents" to decide whether to go + # deeper. + CONTAINER_WIDGETS = [ + :Frame, + :RadioButtonGroup, + :VBox, + :HBox, + :MarginBox, + :MinWidth, + :MinHeight, + :MinSize, + :Left, + :Right, + :Top, + :Bottom, + :HCenter, + :VCenter, + :HVCenter, + :HSquash, + :VSquash, + :HVSquash, + :HWeight, + :VWeight, + :DumbTab, + :ReplacePoint + ].freeze + # local functions # Push the settings of the currently run dialog to the stack @@ -113,49 +113,35 @@ def PopSettings # @param widgets [Hash{String => ::CWM::WidgetHash}] widget name -> widget description # @return [::CWM::UITerm] updated term ready to be used as a dialog def ProcessTerm(t, widgets) - t = deep_copy(t) - widgets = deep_copy(widgets) - args = Builtins.size(t) - return deep_copy(t) if args == 0 - ret = Builtins.toterm( - Builtins.substring(Builtins.sformat("%1", Builtins.symbolof(t)), 1) - ) - id_frame = false - index = 0 - current = Builtins.symbolof(t) - while Ops.less_than(index, args) - arg = Ops.get(t, index) - # FIXME: still there is a problem for frames without label - if current == :Frame && index == 0 # no action + return t if t.empty? + ret = Yast::Term.new(t.value) + + is_frame = t.value == :Frame + is_id_frame = false + + t.each.with_index do |arg, index| + if is_frame && index == 0 # no action # frame can have id and also label, so mark if id is used - id_frame = true if arg.is_a?(Yast::Term) && arg.value == :id + is_id_frame = arg.is_a?(Yast::Term) && arg.value == :id Builtins.y2debug("Leaving untouched %1", arg) - elsif current == :Frame && index == 1 && id_frame && arg.is_a?(::String) # no action - id_frame = false + elsif is_frame && index == 1 && is_id_frame && arg.is_a?(::String) # no action Builtins.y2debug("Leaving untouched %1", arg) - elsif Ops.is_term?(arg) && !arg.nil? # recurse - s = Builtins.symbolof(Convert.to_term(arg)) - if Builtins.contains(@ContainerWidgets, s) - arg = ProcessTerm(Convert.to_term(arg), widgets) + elsif Ops.is_term?(arg) # recurse + if CONTAINER_WIDGETS.include?(arg.value) + arg = ProcessTerm(arg, widgets) end elsif Ops.is_string?(arg) # action Builtins.y2error("find string '#{arg}' without associated widget in StringTerm #{t.inspect}") unless widgets[arg] Builtins.y2milestone("Known widgets #{widgets.inspect}") unless widgets[arg] - arg = Ops.get_term( - widgets, - [Convert.to_string(arg), "widget"], - VBox() - ) - s = Builtins.symbolof(arg) - if Builtins.contains(@ContainerWidgets, s) + arg = widgets.fetch(arg, {}).fetch("widget") { VBox() } + if CONTAINER_WIDGETS.include?(arg.value) arg = ProcessTerm(arg, widgets) end end - ret = Builtins.add(ret, arg) - index = Ops.add(index, 1) + ret << arg end - deep_copy(ret) + ret end # Process term with the dialog, return all strings. @@ -172,9 +158,8 @@ def StringsOfTerm(t) current = Builtins.symbolof(t) if current == :Frame && index == 0 # no action Builtins.y2debug("Leaving untouched %1", arg) - elsif Ops.is_term?(arg) && !arg.nil? # recurse - s = Builtins.symbolof(Convert.to_term(arg)) - if Builtins.contains(@ContainerWidgets, s) + elsif Ops.is_term?(arg) + if CONTAINER_WIDGETS.include?(arg.value) rets = Ops.add(rets, StringsOfTerm(Convert.to_term(arg))) end elsif Ops.is_string?(arg) # action @@ -751,15 +736,13 @@ def MergeHelps(widgets) # @return [::CWM::UITerm] updated term ready to be used as a dialog def PrepareDialog(dialog, widgets) Builtins.y2milestone("PrepDia BEG %1", t1 = Time.now.to_f) - dialog = deep_copy(dialog) - widgets = deep_copy(widgets) - args = Builtins.size(dialog) - return deep_copy(dialog) if args == 0 - m = Builtins.listmap(widgets) do |w| - widget_key = Ops.get_string(w, "_cwm_key", "") - { widget_key => w } - end + return dialog.clone if dialog.empty? + m = widgets.map do |w| + widget_key = w.fetch("_cwm_key", "") + [widget_key, w] + end.to_h t = ProcessTerm(dialog, m) + t = deep_copy(t) Builtins.y2milestone("PrepDia END %1; %2", t2 = Time.now.to_f, t2 - t1) t end @@ -929,6 +912,8 @@ def SetValidationFailedHandler(handler) # Useful mainly when some widget returns an event that should not trigger the storing, # like a reset button or a redrawing. It will skip also validation, because it is not needed # as nothing is stored. + # @param disable_buttons [Array] buttons to disable: + # "back_button", "abort_button", "next_button" # @param next_handler [Proc] handler that is called after clicking on next. If it returns false, # then it does not go next. If it returns true, then :next symbol is returned. If handler is not # defined, then it acts like if it returns true. @@ -1019,6 +1004,7 @@ def ShowAndRun(settings) # @param [Hash] fallback map initialize/save/handle fallbacks if not specified # with the widgets. # @return [Symbol] wizard sequencer symbol + # @deprecated Use {#show} or {#ShowAndRun} def ShowAndRunOrig(widget_names, widget_descr, contents, caption, back_button, next_button, fallback) widget_names = deep_copy(widget_names) widget_descr = deep_copy(widget_descr) diff --git a/library/cwm/test/cwm_test.rb b/library/cwm/test/cwm_test.rb index 7eafab339..06da48744 100755 --- a/library/cwm/test/cwm_test.rb +++ b/library/cwm/test/cwm_test.rb @@ -193,6 +193,8 @@ def generic_save(_key, _event) end end + # Used by many packages. All known uses are of the form + # `contents = CWM.PrepareDialog(contents, ...)` describe "#PrepareDialog" do it "returns early if the term is empty" do expect(subject).to_not receive(:ProcessTerm) @@ -206,6 +208,7 @@ def generic_save(_key, _event) end # tested via its adapter PrepareDialog + # YAY, a private method describe "#ProcessTerm" do it "replaces string ids with UI terms" do ret = subject.PrepareDialog(test_stringterm, created_widgets) From f3c57236c0ca4d4eee7dfefd616bb60d99566ee1 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Thu, 11 Oct 2018 15:15:04 +0200 Subject: [PATCH 182/223] Removed the remaining deep_copy from PrepareDialog because all known callers are safe --- library/cwm/src/modules/CWM.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index 7ae1d3836..307550bc2 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -742,7 +742,6 @@ def PrepareDialog(dialog, widgets) [widget_key, w] end.to_h t = ProcessTerm(dialog, m) - t = deep_copy(t) Builtins.y2milestone("PrepDia END %1; %2", t2 = Time.now.to_f, t2 - t1) t end From af4fefc9cbb6b4e93fc2e0ea7a37d744675767b3 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Thu, 11 Oct 2018 16:21:33 +0200 Subject: [PATCH 183/223] A redundant deep_copy in handleWidgets --- library/cwm/src/modules/CWM.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index 307550bc2..d1ba998a0 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -344,7 +344,6 @@ def initWidgets(widgets) # @param [Hash] event_descr map event that occured # @return [Symbol] modified action (sometimes may be needed) or nil def handleWidgets(widgets, event_descr) - widgets = deep_copy(widgets) event_descr = deep_copy(event_descr) ret = nil Builtins.foreach(widgets) do |w| From 42d1560d7bfe09c9c16f0d9cbc5dbf584aaa2838 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Tue, 16 Oct 2018 16:26:56 +0200 Subject: [PATCH 184/223] More CWM test coverage --- library/cwm/src/modules/CWM.rb | 17 +++++-- library/cwm/test/cwm_test.rb | 93 +++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 19 deletions(-) diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index d1ba998a0..9d64d82f8 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -326,7 +326,7 @@ def initWidgets(widgets) ) end # set initial values - @processed_widget = deep_copy(w) + self.processed_widget = deep_copy(w) toEval = Convert.convert( Ops.get(w, "init"), from: "any", @@ -348,7 +348,7 @@ def handleWidgets(widgets, event_descr) ret = nil Builtins.foreach(widgets) do |w| if ret.nil? - @processed_widget = deep_copy(w) + self.processed_widget = deep_copy(w) events = Ops.get_list(w, "handle_events", []) toEval = Convert.convert( Ops.get(w, "handle"), @@ -374,7 +374,7 @@ def saveWidgets(widgets, event) widgets = deep_copy(widgets) event = deep_copy(event) Builtins.foreach(widgets) do |w| - @processed_widget = deep_copy(w) + self.processed_widget = deep_copy(w) toEval = Convert.convert( Ops.get(w, "store"), from: "any", @@ -392,7 +392,7 @@ def saveWidgets(widgets, event) def cleanupWidgets(widgets) widgets = deep_copy(widgets) Builtins.foreach(widgets) do |w| - @processed_widget = deep_copy(w) + self.processed_widget = deep_copy(w) toEval = Convert.convert( Ops.get(w, "cleanup"), from: "any", @@ -630,7 +630,7 @@ def prepareWidget(widget_descr) def validateWidget(widget, event, key) widget = deep_copy(widget) event = deep_copy(event) - @processed_widget = deep_copy(widget) + self.processed_widget = deep_copy(widget) failed = false val_type = Ops.get_symbol(widget, "validate_type") if val_type == :function || val_type == :function_no_popup @@ -1107,6 +1107,13 @@ def widgets_contents(contents) res end + + private + + # such an accessor enables testing + def processed_widget=(w) + @processed_widget = w + end end CWM = CWMClass.new diff --git a/library/cwm/test/cwm_test.rb b/library/cwm/test/cwm_test.rb index 06da48744..bca7dc724 100755 --- a/library/cwm/test/cwm_test.rb +++ b/library/cwm/test/cwm_test.rb @@ -81,6 +81,10 @@ def generic_save(_key, _event) method(:w2_validate), "boolean (string, map)" ) + }, + "w3" => { + "widget" => :custom, + "custom_widget" => HBox("w2", "w2") } } end @@ -134,10 +138,20 @@ def generic_save(_key, _event) end # used via GetProcessedWidget by yast2-slp-server and yast2 - xit "sets @processed_widget" do + it "sets @processed_widget" do + allow(self).to receive(:w1_init) + allow(self).to receive(:generic_init) + expect(subject).to receive(:processed_widget=).twice + subject.initWidgets(run_widgets) end - xit "sets ValidChars" do + it "sets ValidChars" do + allow(self).to receive(:w1_init) + allow(self).to receive(:generic_init) + widgets = deep_copy(run_widgets) + widgets[0]["valid_chars"] = "ABC" + expect(Yast::UI).to receive(:ChangeWidget).with(Id("w1"), :ValidChars, "ABC") + subject.initWidgets(widgets) end end @@ -150,7 +164,11 @@ def generic_save(_key, _event) end # used via GetProcessedWidget by yast2-slp-server and yast2 - xit "sets @processed_widget" do + it "sets @processed_widget" do + allow(self).to receive(:generic_save) + allow(self).to receive(:w2_store) + expect(subject).to receive(:processed_widget=).twice + subject.saveWidgets(run_widgets, "ID" => :event) end end @@ -168,10 +186,19 @@ def generic_save(_key, _event) expect(subject.handleWidgets(run_widgets, "ID" => :event)).to eq(:foo) end - xit "sets @processed_widget" do + it "sets @processed_widget" do + allow(self).to receive(:w1_handle).and_return(nil) + allow(self).to receive(:w2_handle).and_return(nil) + expect(subject).to receive(:processed_widget=).twice + subject.handleWidgets(run_widgets, "ID" => :event) end - xit "filters the events if 'handle_events' is specified" do + it "filters the events if 'handle_events' is specified" do + expect(self).to_not receive(:w1_handle) + allow(self).to receive(:w2_handle) + widgets = deep_copy(run_widgets) + widgets[0]["handle_events"] = [:special_event] + subject.handleWidgets(widgets, "ID" => :event) end end @@ -182,14 +209,26 @@ def generic_save(_key, _event) expect(subject.validateWidgets(run_widgets, "ID" => :event)).to eq(true) end - it "breaks the loop if a handler returns false" do - expect(self).to receive(:w1_validate).with("w1", "ID" => :event).and_return(false) - expect(self).to_not receive(:w2_validate) - expect(subject.validateWidgets(run_widgets, "ID" => :event)).to eq(false) - end - - # SetValidationFailedHandler - xit "calls validation_failed_handler if..." do + context "if a handler returns false" do + before do + expect(self).to receive(:w1_validate).with("w1", "ID" => :event).and_return(false) + end + + it "breaks the loop if a handler returns false" do + expect(self).to_not receive(:w2_validate) + expect(subject.validateWidgets(run_widgets, "ID" => :event)).to eq(false) + end + + # SetValidationFailedHandler + it "calls validation_failed_handler if it has been set" do + called = false + handler = -> { called = true } + subject.SetValidationFailedHandler(handler) + + subject.validateWidgets(run_widgets, "ID" => :event) + # we cannot set an expectation on `handler` because a copy is made + expect(called).to eq(true) + end end end @@ -205,6 +244,24 @@ def generic_save(_key, _event) expect(subject).to receive(:ProcessTerm).with(test_stringterm, Hash) subject.PrepareDialog(test_stringterm, created_widgets) end + + # Test that *block* does not modify its argument, + # by storing a deep_copy of it and expecting equality afterwards + # @param value [Object] + # @yieldparam a copy of *value* + def expect_not_modified(value, &block) + copy = deep_copy(value) + block.call(copy) + expect(copy).to eq(value) + end + + it "does not modify its arguments" do + expect_not_modified(test_stringterm) do |contents| + expect_not_modified(created_widgets) do |widgets| + subject.PrepareDialog(contents, widgets) + end + end + end end # tested via its adapter PrepareDialog @@ -216,7 +273,15 @@ def generic_save(_key, _event) expect(w1).to eq(CheckBox(Id("w1"), Opt(:notify, :immediate), "Check&Box")) end - xit "recurses into container widgets" do + it "recurses into container widgets" do + created_widgets = subject.CreateWidgets(["w2", "w3"], test_widgets) + ret = subject.PrepareDialog(VBox("w2", "w3"), created_widgets) + w3 = ret.params[1] + expected = HBox( + InputField(Id("w2"), Opt(:hstretch), "Text&Entry"), + InputField(Id("w2"), Opt(:hstretch), "Text&Entry") + ) + expect(w3).to eq(expected) end it "leaves Frame titles alone" do From 6f9ad9b8066c5095ffd5613e604d53689aabbdc6 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Thu, 18 Oct 2018 14:54:49 +0200 Subject: [PATCH 185/223] Revert "FIXME: revert: measure time spent in certain methods" This reverts commit 1af218b08de61168d8fd28ce58bef1847ff31556. --- library/cwm/src/lib/cwm/replace_point.rb | 2 -- library/cwm/src/modules/CWM.rb | 10 ++-------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/library/cwm/src/lib/cwm/replace_point.rb b/library/cwm/src/lib/cwm/replace_point.rb index d8cf76845..232428223 100644 --- a/library/cwm/src/lib/cwm/replace_point.rb +++ b/library/cwm/src/lib/cwm/replace_point.rb @@ -36,7 +36,6 @@ def init # handled. # @param widget [CWM::AbstractWidget] widget to display and process events def replace(widget) - Yast::Builtins.y2milestone("REPLACE BEG %1", t1 = Time.now.to_f) widgets = Yast::CWM.widgets_in_contents([widget]) @widgets_hash = widgets.map { |w| Yast::CWM.prepareWidget(w.cwm_definition) } # VBox as CWM ignore top level term and process string inside it, @@ -46,7 +45,6 @@ def replace(widget) Yast::CWM.initWidgets(@widgets_hash) @widget = widget refresh_help - Yast::Builtins.y2milestone("REPLACE END %1; %2", t2 = Time.now.to_f, t2 - t1) end # Passes to replace point content diff --git a/library/cwm/src/modules/CWM.rb b/library/cwm/src/modules/CWM.rb index 9d64d82f8..3941e482f 100644 --- a/library/cwm/src/modules/CWM.rb +++ b/library/cwm/src/modules/CWM.rb @@ -696,7 +696,6 @@ def validateWidgets(widgets, event) # @param [Hash ] of maps representing widgets def CreateWidgets(names, source) - Builtins.y2milestone("CreaWid BEG %1", t1 = Time.now.to_f) names = deep_copy(names) source = deep_copy(source) ValidateMaps(source) # FIXME: find better place @@ -709,9 +708,7 @@ def CreateWidgets(names, source) deep_copy(m) end ret = Builtins.maplist(ret) { |w| prepareWidget(w) } - r = deep_copy(ret) - Builtins.y2milestone("CreaWid END %1; %2", t2 = Time.now.to_f, t2 - t1) - r + deep_copy(ret) end # Merge helps from the widgets @@ -734,15 +731,12 @@ def MergeHelps(widgets) # @param widgets [Array<::CWM::WidgetHash>] list of widget description maps # @return [::CWM::UITerm] updated term ready to be used as a dialog def PrepareDialog(dialog, widgets) - Builtins.y2milestone("PrepDia BEG %1", t1 = Time.now.to_f) return dialog.clone if dialog.empty? m = widgets.map do |w| widget_key = w.fetch("_cwm_key", "") [widget_key, w] end.to_h - t = ProcessTerm(dialog, m) - Builtins.y2milestone("PrepDia END %1; %2", t2 = Time.now.to_f, t2 - t1) - t + ProcessTerm(dialog, m) end # Replace help for a particular widget From 5b6fcc7dfc4521a27d2dc2e33839c2100966b881 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Thu, 18 Oct 2018 15:31:28 +0200 Subject: [PATCH 186/223] version + changelog --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index c7f5ff314..04b09cb63 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Thu Oct 18 13:28:51 UTC 2018 - mvidner@suse.com + +- Small CWM optimization by avoiding deep_copy on big data + (bsc#1112402). +- 4.1.26 + ------------------------------------------------------------------- Wed Oct 17 15:17:04 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 1860312eb..cb1eab1b5 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.25 +Version: 4.1.26 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From b047e442ba6cdb4506d6b08e1a23409c7075bc74 Mon Sep 17 00:00:00 2001 From: Arvin Schnell Date: Thu, 18 Oct 2018 21:34:08 +0200 Subject: [PATCH 187/223] adapted to extended output of snapper (fate#326479, bsc#1111831) --- library/system/src/lib/yast2/fs_snapshot.rb | 8 ++++---- .../system/test/fixtures/empty-snapper-list.txt | 4 ++-- library/system/test/fixtures/snapper-list.txt | 14 +++++++------- library/system/test/fs_snapshot_test.rb | 2 +- package/yast2.changes | 6 ++++++ package/yast2.spec | 2 +- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/library/system/src/lib/yast2/fs_snapshot.rb b/library/system/src/lib/yast2/fs_snapshot.rb index ecdc35553..e34b5d755 100644 --- a/library/system/src/lib/yast2/fs_snapshot.rb +++ b/library/system/src/lib/yast2/fs_snapshot.rb @@ -73,8 +73,8 @@ class FsSnapshot FIND_CONFIG_CMD = "/usr/bin/snapper --no-dbus --root=%{root} list-configs | grep \"^root \" >/dev/null".freeze CREATE_SNAPSHOT_CMD = "/usr/lib/snapper/installation-helper --step 5 --root-prefix=%{root} --snapshot-type %{snapshot_type} --description \"%{description}\"".freeze - LIST_SNAPSHOTS_CMD = "LANG=en_US.UTF-8 /usr/bin/snapper --no-dbus --root=%{root} list".freeze - VALID_LINE_REGEX = /\A\w+\s+\| \d+/ + LIST_SNAPSHOTS_CMD = "LANG=en_US.UTF-8 /usr/bin/snapper --no-dbus --root=%{root} list --disable-used-space".freeze + VALID_LINE_REGEX = /\A\s*\d+[-+*]?\s*\|\s*\w+/ # Predefined snapshot cleanup strategies (the user can define custom ones, too) CLEANUP_STRATEGY = { number: "number", timeline: "timeline" }.freeze @@ -259,7 +259,7 @@ def all log.info("Retrieving snapshots list: #{LIST_SNAPSHOTS_CMD} returned: #{out}") lines.each_with_object([]) do |line, snapshots| data = line.split("|").map(&:strip) - next if data[1] == "0" # Ignores 'current' snapshot (id = 0) because it's not a real snapshot + next if data[0] == "0" # Ignores 'current' snapshot (id = 0) because it's not a real snapshot begin timestamp = DateTime.parse(data[3]) rescue ArgumentError @@ -267,7 +267,7 @@ def all timestamp = nil end previous_number = data[2] == "" ? nil : data[2].to_i - snapshots << new(data[1].to_i, data[0].to_sym, previous_number, timestamp, + snapshots << new(data[0].to_i, data[1].to_sym, previous_number, timestamp, data[4], data[5].to_sym, data[6]) end end diff --git a/library/system/test/fixtures/empty-snapper-list.txt b/library/system/test/fixtures/empty-snapper-list.txt index fa4b8a6ac..78d88e976 100644 --- a/library/system/test/fixtures/empty-snapper-list.txt +++ b/library/system/test/fixtures/empty-snapper-list.txt @@ -1,2 +1,2 @@ -Type | # | Pre # | Date | User | Cleanup | Description | Userdata --------+----+-------+----------------------------------+------+---------+--------------+-------------- + # | Type | Pre # | Date | User | Cleanup | Description | Userdata +---+--------+-------+----------------------------------+------+---------+--------------+-------------- diff --git a/library/system/test/fixtures/snapper-list.txt b/library/system/test/fixtures/snapper-list.txt index f00e20d2a..561319dcb 100644 --- a/library/system/test/fixtures/snapper-list.txt +++ b/library/system/test/fixtures/snapper-list.txt @@ -1,7 +1,7 @@ -Type | # | Pre # | Date | User | Cleanup | Description | Userdata --------+----+-------+----------------------------------+------+---------+--------------+-------------- -single | 0 | | | root | | current | -pre | 1 | | Wed 13 May 2015 04:14:14 PM WEST | root | number | zypp(y2base) | important=no -pre | 3 | | Wed 13 May 2015 05:01:47 PM WEST | root | number | zypp(zypper) | important=no -post | 4 | 3 | Wed 13 May 2015 05:03:13 PM WEST | root | number | zypp(zypper) | important=no -single | 5 | | Wed 13 May 2015 05:11:25 PM WEST | root | | | + # | Type | Pre # | Date | User | Cleanup | Description | Userdata +----+--------+-------+----------------------------------+------+---------+--------------+-------------- + 0 | single | | | root | | current | + 1 | pre | | Wed 13 May 2015 04:14:14 PM WEST | root | number | zypp(y2base) | important=no + 3 | pre | | Wed 13 May 2015 05:01:47 PM WEST | root | number | zypp(zypper) | important=no + 4 | post | 3 | Wed 13 May 2015 05:03:13 PM WEST | root | number | zypp(zypper) | important=no +15* | single | | Wed 13 May 2015 05:11:25 PM WEST | root | | | diff --git a/library/system/test/fs_snapshot_test.rb b/library/system/test/fs_snapshot_test.rb index 0493df177..87462f9cc 100755 --- a/library/system/test/fs_snapshot_test.rb +++ b/library/system/test/fs_snapshot_test.rb @@ -10,7 +10,7 @@ def logger FIND_CONFIG = "/usr/bin/snapper --no-dbus --root=/ list-configs | grep \"^root \" >/dev/null".freeze FIND_IN_ROOT_CONFIG = "/usr/bin/snapper --no-dbus --root=/mnt list-configs | grep \"^root \" >/dev/null".freeze - LIST_SNAPSHOTS = "LANG=en_US.UTF-8 /usr/bin/snapper --no-dbus --root=/ list".freeze + LIST_SNAPSHOTS = "LANG=en_US.UTF-8 /usr/bin/snapper --no-dbus --root=/ list --disable-used-space".freeze let(:dummy_snapshot) { double("snapshot") } diff --git a/package/yast2.changes b/package/yast2.changes index 04b09cb63..ed108bd2f 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Thu Oct 18 21:29:45 CEST 2018 - aschnell@suse.com + +- adapted to extended output of snapper (fate#326479, bsc#1111831) +- 4.1.27 + ------------------------------------------------------------------- Thu Oct 18 13:28:51 UTC 2018 - mvidner@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index cb1eab1b5..21f4a2795 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.26 +Version: 4.1.27 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 5e55a9675dbf6a0683ad85c68c00a28420477556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Tue, 23 Oct 2018 09:55:21 +0100 Subject: [PATCH 188/223] Added API method for modifying masquerade --- .../src/lib/y2firewall/firewalld/api/zones.rb | 11 ++++++++ .../y2firewall/firewalld/api/zones_test.rb | 27 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/library/network/src/lib/y2firewall/firewalld/api/zones.rb b/library/network/src/lib/y2firewall/firewalld/api/zones.rb index 66ad46b85..80baf7a4e 100644 --- a/library/network/src/lib/y2firewall/firewalld/api/zones.rb +++ b/library/network/src/lib/y2firewall/firewalld/api/zones.rb @@ -283,6 +283,17 @@ def remove_masquerade(zone, permanent: permanent?) modify_command("--zone=#{zone}", "--remove-masquerade", permanent: permanent) end + # Enable or disable masquerade in the zone + # + # @param zone [String] The firewall zone + # @param enabled [Boolean] whether masquerade should be enabled or not + # @return [Boolean] whether masquerade was modified or not + def modify_masquerade(zone, enabled) + method = enabled ? "add_masquerade" : "remove_masquerade" + + public_send(method, zone, permanent: !offline?) + end + # Full name or short description of the zone # # @param zone [String] The firewall zone diff --git a/library/network/test/y2firewall/firewalld/api/zones_test.rb b/library/network/test/y2firewall/firewalld/api/zones_test.rb index 636a88e57..0c092f11b 100755 --- a/library/network/test/y2firewall/firewalld/api/zones_test.rb +++ b/library/network/test/y2firewall/firewalld/api/zones_test.rb @@ -54,6 +54,33 @@ end end + describe "#modify_masquerade" do + it "does nothing if the masquerade is already at the requested state" do + allow(subject).to receive(:masquerade_enabled?).and_return(true) + + expect(api).to_not receive(:modify_command) + .with("--zone=test", "--add-masquerade", permanent: api.permanent?) + + subject.modify_masquerade("test", true) + end + + it "enables masquerade from the zone if enabled is true" do + allow(subject).to receive(:masquerade_enabled?).and_return(false) + expect(api).to receive(:modify_command) + .with("--zone=test", "--add-masquerade", permanent: api.permanent?) + + subject.modify_masquerade("test", true) + end + + it "removes masquerade from the zone if enabled is true" do + allow(subject).to receive(:masquerade_enabled?).and_return(true) + expect(api).to receive(:modify_command) + .with("--zone=test", "--remove-masquerade", permanent: api.permanent?) + + subject.modify_masquerade("test", false) + end + end + describe "#description" do let(:description) { "This is the long description of the test zone." } it "obtains the zone long description" do From f05a98bb0aa5ae9b51fb448eb5226e373b1014cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Tue, 23 Oct 2018 10:00:18 +0100 Subject: [PATCH 189/223] Bump version & changelog. --- package/yast2.changes | 8 ++++++++ package/yast2.spec | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index fadb268b1..ff1fc8771 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Tue Oct 23 08:55:40 UTC 2018 - knut.anderssen@suse.com + +- Network (Firewall): Added modify_masquerade method to zones API + unifying the way changes are applied to single value attributes. + (bsc#1112547) +- 4.0.103 + ------------------------------------------------------------------- Wed Oct 17 15:17:04 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index c241f8bc6..405cf0521 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.0.102 +Version: 4.0.103 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From a50014c26cba64eeef138408d0cab90eef5db1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 24 Oct 2018 13:28:27 +0100 Subject: [PATCH 190/223] Make current state methods public --- .../src/modules/CWMFirewallInterfaces.rb | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/library/network/src/modules/CWMFirewallInterfaces.rb b/library/network/src/modules/CWMFirewallInterfaces.rb index c016ce427..60b1d9f5c 100644 --- a/library/network/src/modules/CWMFirewallInterfaces.rb +++ b/library/network/src/modules/CWMFirewallInterfaces.rb @@ -904,55 +904,6 @@ def Modified firewalld.modified? end - publish function: :InitAllowedInterfaces, type: "void (list )" - publish function: :StoreAllowedInterfaces, type: "void (list )" - publish function: :InterfacesInit, type: "void (map , string)" - publish function: :InterfacesHandle, type: "symbol (map , string, map)" - publish function: :InterfacesStore, type: "void (map , string, map)" - publish function: :InterfacesValidate, type: "boolean (map , string, map)" - publish function: :InterfacesInitWrapper, type: "void (string)" - publish function: :InterfacesHandleWrapper, type: "symbol (string, map)" - publish function: :InterfacesStoreWrapper, type: "void (string, map)" - publish function: :InterfacesValidateWrapper, type: "boolean (string, map)" - publish function: :CreateInterfacesWidget, type: "map (map )" - publish function: :DisplayDetailsPopup, type: "symbol (map )" - publish function: :OpenFirewallInit, type: "void (map , string)" - publish function: :OpenFirewallStore, type: "void (map , string, map)" - publish function: :OpenFirewallHandle, type: "symbol (map , string, map)" - publish function: :OpenFirewallInitWrapper, type: "void (string)" - publish function: :OpenFirewallStoreWrapper, type: "void (string, map)" - publish function: :OpenFirewallHandleWrapper, type: "symbol (string, map)" - publish function: :OpenFirewallModified, type: "boolean (string)" - publish function: :EnableOpenFirewallWidget, type: "void ()" - publish function: :DisableOpenFirewallWidget, type: "void ()" - publish function: :OpenFirewallWidgetExists, type: "boolean ()" - publish function: :OpenFirewallHelpTemplate, type: "string (boolean)" - publish function: :OpenFirewallHelp, type: "string (boolean)" - publish function: :CreateOpenFirewallWidget, type: "map (map )" - publish function: :Modified, type: "boolean ()" - - private - - # Return whether the '_cwm_open_firewall' widget exists or not logging the - # error in case of non-existence. - # - # @return [Boolean] true if the open firewall widget exists - def open_firewall_widget? - unless UI.WidgetExists(Id("_cwm_open_firewall")) - log.error("Widget _cwm_open_firewall does not exist") - return false - end - - true - end - - # Return an instance of Y2Firewall::Firewalld - # - # @return [Y2Firewall::Firewalld] a firewalld instance - def firewalld - Y2Firewall::Firewalld.instance - end - # Return the current status of the firewall related to the interfaces # opened or available # @@ -1003,6 +954,55 @@ def firewall_status_label(status) end end + publish function: :InitAllowedInterfaces, type: "void (list )" + publish function: :StoreAllowedInterfaces, type: "void (list )" + publish function: :InterfacesInit, type: "void (map , string)" + publish function: :InterfacesHandle, type: "symbol (map , string, map)" + publish function: :InterfacesStore, type: "void (map , string, map)" + publish function: :InterfacesValidate, type: "boolean (map , string, map)" + publish function: :InterfacesInitWrapper, type: "void (string)" + publish function: :InterfacesHandleWrapper, type: "symbol (string, map)" + publish function: :InterfacesStoreWrapper, type: "void (string, map)" + publish function: :InterfacesValidateWrapper, type: "boolean (string, map)" + publish function: :CreateInterfacesWidget, type: "map (map )" + publish function: :DisplayDetailsPopup, type: "symbol (map )" + publish function: :OpenFirewallInit, type: "void (map , string)" + publish function: :OpenFirewallStore, type: "void (map , string, map)" + publish function: :OpenFirewallHandle, type: "symbol (map , string, map)" + publish function: :OpenFirewallInitWrapper, type: "void (string)" + publish function: :OpenFirewallStoreWrapper, type: "void (string, map)" + publish function: :OpenFirewallHandleWrapper, type: "symbol (string, map)" + publish function: :OpenFirewallModified, type: "boolean (string)" + publish function: :EnableOpenFirewallWidget, type: "void ()" + publish function: :DisableOpenFirewallWidget, type: "void ()" + publish function: :OpenFirewallWidgetExists, type: "boolean ()" + publish function: :OpenFirewallHelpTemplate, type: "string (boolean)" + publish function: :OpenFirewallHelp, type: "string (boolean)" + publish function: :CreateOpenFirewallWidget, type: "map (map )" + publish function: :Modified, type: "boolean ()" + + private + + # Return whether the '_cwm_open_firewall' widget exists or not logging the + # error in case of non-existence. + # + # @return [Boolean] true if the open firewall widget exists + def open_firewall_widget? + unless UI.WidgetExists(Id("_cwm_open_firewall")) + log.error("Widget _cwm_open_firewall does not exist") + return false + end + + true + end + + # Return an instance of Y2Firewall::Firewalld + # + # @return [Y2Firewall::Firewalld] a firewalld instance + def firewalld + Y2Firewall::Firewalld.instance + end + def zone_services(services) services_status = {} From e42dae002bb8eeb2da0e08c57c5a9bf017a81ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 24 Oct 2018 13:52:05 +0100 Subject: [PATCH 191/223] Bump version & changelog --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index 16d9be9d2..b215f0094 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Oct 24 12:48:18 UTC 2018 - knut.anderssen@suse.com + +- CWMFirewallInterfaces: make some "current state" methods public + needed by yast2-rmt (fate#326634) +- 4.1.29 + ------------------------------------------------------------------- Tue Oct 23 08:55:40 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 2d8ebee44..d904d2724 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.28 +Version: 4.1.29 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From e07a34acb7e44e1f428435763af902247cf15353 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 24 Oct 2018 17:53:09 +0200 Subject: [PATCH 192/223] Use LSB compliment path for temporary mount and ensure it exists --- library/control/src/modules/Installation.rb | 19 ++++++++++---- library/control/test/Makefile.am | 1 + library/control/test/installation_test.rb | 28 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100755 library/control/test/installation_test.rb diff --git a/library/control/src/modules/Installation.rb b/library/control/src/modules/Installation.rb index 3ee7fcd82..ec68a7068 100644 --- a/library/control/src/modules/Installation.rb +++ b/library/control/src/modules/Installation.rb @@ -40,6 +40,9 @@ module Yast class InstallationClass < Module + # usual mountpoint for the source (i.e. CD) + SOURCEDIR = "/run/YaST2/mount" + def main Yast.import "Stage" Yast.import "Linuxrc" @@ -56,10 +59,6 @@ def main # set to "/" when the SCR is restarted in the target system. @scr_destdir = "/" - # usual mountpoint for the source (i.e. CD) - - @sourcedir = "/var/adm/mount" - @yast2dir = "/var/lib/YaST2" @mountlog = Ops.add(Directory.logdir, "/y2logMount") @@ -287,10 +286,19 @@ def no_x11 @_no_x11 end + # Gets directory suitable for source mount. It ensures that directory exists. + # It does not ensure unmounting previous content. + # + # @return [String] + def sourcedir + ::FileUtils.mkdir_p(SOURCEDIR) unless ::File.exist?(SOURCEDIR) + + SOURCEDIR + end + publish variable: :scr_handle, type: "integer" publish variable: :destdir, type: "string" publish variable: :scr_destdir, type: "string" - publish variable: :sourcedir, type: "string" publish variable: :yast2dir, type: "string" publish variable: :mountlog, type: "string" publish variable: :encoding, type: "string" @@ -326,6 +334,7 @@ def no_x11 publish function: :x11_setup_needed, type: "boolean ()" publish function: :text_fallback, type: "boolean ()" publish function: :no_x11, type: "boolean ()" + publish function: :sourcedir, type: "string ()" end Installation = InstallationClass.new diff --git a/library/control/test/Makefile.am b/library/control/test/Makefile.am index 5374d07f4..8edf30bbe 100644 --- a/library/control/test/Makefile.am +++ b/library/control/test/Makefile.am @@ -1,5 +1,6 @@ TESTS = \ InstExtensionImage_test.rb \ + instalation_test.rb \ ProductFeatures_test.rb \ workflow_manager_test.rb diff --git a/library/control/test/installation_test.rb b/library/control/test/installation_test.rb new file mode 100755 index 000000000..21593cb25 --- /dev/null +++ b/library/control/test/installation_test.rb @@ -0,0 +1,28 @@ +#! /usr/bin/env rspec + +require_relative "test_helper" + +Yast.import "Installation" + +describe Yast::Installation do + subject { Yast::Installation } + + describe ".sourcedir" do + before do + allow(::File).to receive(:exist?).and_return(true) + end + + it "returns string" do + expect(subject.sourcedir).to eq "/run/YaST2/mount" + end + + it "ensures that directory exists" do + expect(::File).to receive(:exist?).and_return(false) + expect(::FileUtils).to receive(:mkdir_p) + + subject.sourcedir + end + end +end + + From 2db1c26014db3df36309c79e5735f575d93e218d Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 24 Oct 2018 17:58:37 +0200 Subject: [PATCH 193/223] Changes --- package/yast2.changes | 9 ++++++++- package/yast2.spec | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/package/yast2.changes b/package/yast2.changes index b215f0094..51ae6af95 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Oct 24 15:55:08 UTC 2018 - jreidinger@suse.com + +- Ensure that Installation.sourcedir exists (bsc#1097700) +- Use LSB compliant Installation.sourcedir +- 4.1.30 + ------------------------------------------------------------------- Wed Oct 24 12:48:18 UTC 2018 - knut.anderssen@suse.com @@ -24,7 +31,7 @@ Thu Oct 18 13:28:51 UTC 2018 - mvidner@suse.com - Small CWM optimization by avoiding deep_copy on big data (bsc#1112402). -- 4.1.26 +- 4.1.26 ------------------------------------------------------------------- Wed Oct 17 15:17:04 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index d904d2724..bc7ec747c 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.29 +Version: 4.1.30 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 2e1c61866976e8c72b8894beee9b860d81227f25 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 24 Oct 2018 18:05:38 +0200 Subject: [PATCH 194/223] make rubocop happy --- library/control/src/modules/Installation.rb | 2 +- library/control/test/installation_test.rb | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/library/control/src/modules/Installation.rb b/library/control/src/modules/Installation.rb index ec68a7068..7314e9c3f 100644 --- a/library/control/src/modules/Installation.rb +++ b/library/control/src/modules/Installation.rb @@ -41,7 +41,7 @@ module Yast class InstallationClass < Module # usual mountpoint for the source (i.e. CD) - SOURCEDIR = "/run/YaST2/mount" + SOURCEDIR = "/run/YaST2/mount".freeze def main Yast.import "Stage" diff --git a/library/control/test/installation_test.rb b/library/control/test/installation_test.rb index 21593cb25..665f8278a 100755 --- a/library/control/test/installation_test.rb +++ b/library/control/test/installation_test.rb @@ -24,5 +24,3 @@ end end end - - From fd502cc18414df610dc9970e30947cea2fa0937c Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 24 Oct 2018 18:12:28 +0200 Subject: [PATCH 195/223] fix typo --- library/control/test/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/control/test/Makefile.am b/library/control/test/Makefile.am index 8edf30bbe..2bdf35d8d 100644 --- a/library/control/test/Makefile.am +++ b/library/control/test/Makefile.am @@ -1,6 +1,6 @@ TESTS = \ InstExtensionImage_test.rb \ - instalation_test.rb \ + installation_test.rb \ ProductFeatures_test.rb \ workflow_manager_test.rb From 32c619f09534a688d734a27e78e654261898a287 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Thu, 25 Oct 2018 10:41:17 +0200 Subject: [PATCH 196/223] Added flag save_y2logs to control.xml (#852) --- library/control/src/modules/ProductFeatures.rb | 3 ++- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/library/control/src/modules/ProductFeatures.rb b/library/control/src/modules/ProductFeatures.rb index 7b08a1e83..6fd6e0e09 100644 --- a/library/control/src/modules/ProductFeatures.rb +++ b/library/control/src/modules/ProductFeatures.rb @@ -72,7 +72,8 @@ def main # FATE #304865 "base_product_license_directory" => "/etc/YaST2/licenses/base/", "full_system_media_name" => "", - "full_system_download_url" => "" + "full_system_download_url" => "", + "save_y2logs" => true }, "partitioning" => { "use_flexible_partitioning" => false, diff --git a/package/yast2.changes b/package/yast2.changes index 51ae6af95..4d26ab870 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Oct 24 16:55:08 UTC 2018 - schubi@suse.de + +- Added flag save_y2logs to control.xml file in order to save + YaST logs at the end of installation (fate#325737) +- 4.1.31 + ------------------------------------------------------------------- Wed Oct 24 15:55:08 UTC 2018 - jreidinger@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index bc7ec747c..d0d7419c8 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.30 +Version: 4.1.31 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From d89c4d7aa0fe4a15d7730a2297b6a480d28bcf07 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 10:52:49 +0200 Subject: [PATCH 197/223] fix tests --- library/packages/test/slide_show_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/library/packages/test/slide_show_test.rb b/library/packages/test/slide_show_test.rb index 30b432d29..fd7fc9817 100755 --- a/library/packages/test/slide_show_test.rb +++ b/library/packages/test/slide_show_test.rb @@ -10,6 +10,7 @@ describe "Yast::SlideShow" do before(:each) do log.info "--------- Running test ---------" + allow(::File).to receive(:exist?).and_return(true) end TOTAL_PROGRESS_ID = Yast::SlideShowClass::UI_ID::TOTAL_PROGRESS From 6e17576ef93d001d7b5e6a85811faa16f6c3bad6 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 11:03:07 +0200 Subject: [PATCH 198/223] do not create path in slideshow --- library/packages/src/modules/Slides.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/packages/src/modules/Slides.rb b/library/packages/src/modules/Slides.rb index 0527805b0..62ca84258 100644 --- a/library/packages/src/modules/Slides.rb +++ b/library/packages/src/modules/Slides.rb @@ -43,7 +43,7 @@ def main # list of currently known slides, in the order they should be shown @slides = [] # base path to look for slides - @slide_base_path = Ops.add(Installation.sourcedir, "/suse/setup/slide") + @slide_base_path = Ops.add(InstallationClass::SOURCEDIR, "/suse/setup/slide") # path to look for texts of slides @slide_txt_path = "" # path to look for images of slides From 4d51babd653a40c6fb2d28e7884ab0f0426f1356 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 15:24:56 +0200 Subject: [PATCH 199/223] cover view_anymsg by tests --- library/system/src/Makefile.am | 8 +- library/system/src/clients/view_anymsg.rb | 260 +---------------- .../src/lib/yast2/clients/view_anymsg.rb | 276 ++++++++++++++++++ library/system/test/Makefile.am | 1 + .../system/test/clients/view_anymsg_test.rb | 176 +++++++++++ 5 files changed, 461 insertions(+), 260 deletions(-) create mode 100644 library/system/src/lib/yast2/clients/view_anymsg.rb create mode 100755 library/system/test/clients/view_anymsg_test.rb diff --git a/library/system/src/Makefile.am b/library/system/src/Makefile.am index 4a72f1e87..e22bf1371 100644 --- a/library/system/src/Makefile.am +++ b/library/system/src/Makefile.am @@ -36,7 +36,13 @@ ylib_DATA = \ lib/yast2/target_file.rb \ lib/yast2/system_time.rb +ylibclientsdir = @ylibdir@/yast2/clients +ylibclients_DATA = \ + lib/yast2/clients/view_anymsg.rb -EXTRA_DIST = $(module_DATA) $(client_DATA) $(ynclude_DATA) $(scrconf_DATA) $(desktop_DATA) $(ylib_DATA) + + + +EXTRA_DIST = $(module_DATA) $(client_DATA) $(ynclude_DATA) $(scrconf_DATA) $(desktop_DATA) $(ylib_DATA) $(ylibclients_DATA) include $(top_srcdir)/Makefile.am.common diff --git a/library/system/src/clients/view_anymsg.rb b/library/system/src/clients/view_anymsg.rb index 88378d652..fc56429b3 100644 --- a/library/system/src/clients/view_anymsg.rb +++ b/library/system/src/clients/view_anymsg.rb @@ -21,265 +21,7 @@ # you may find current contact information at www.novell.com # # *************************************************************************** -# view_anymsg.ycp -# -# small script for easy /var/log/* and /proc/* viewing -# -# Author: Klaus Kaempf -# -# $Id$ - -require "yast/core_ext" - -# Reads a \n separated list of filenames from -# /var/lib/YaST2/filenames -# Lines starting with "#" are ignored (comments) -# A line starting with "*" is taken as the default filename, the "*" is stripped -# -# All files are listed in an editable combo box, where the user can -# easily switch between files and even add a new file -# -# At finish, the list of filenames is written back to -# /var/lib/YaST2/filenames -# adapting the default line (starting with "*") accordingly. -# -# The default is either given as WFM::Args(0) or is the file last viewed. -module Yast - class ViewAnymsgClient < Client - using Yast::CoreExt::AnsiString - - # [String] Default list of log files - DEFAULT_FILENAMES = [ - "/var/log/boot.log", - "/var/log/messages", - "/var/log/YaST2/y2log" - ].freeze - - def main - Yast.import "UI" - textdomain "base" - - Yast.import "CommandLine" - Yast.import "Directory" - Yast.import "FileUtils" - Yast.import "Label" - - @vardir = Directory.vardir - - # Check if the filename list is present - if !FileUtils.Exists(Ops.add(@vardir, "/filenames")) - SCR.Execute( - path(".target.bash"), - Ops.add( - Ops.add( - Ops.add(Ops.add("/bin/cp ", Directory.ydatadir), "/filenames "), - @vardir - ), - "/filenames" - ) - ) - end - - # get filename list - @filenames = Convert.to_string( - SCR.Read(path(".target.string"), Ops.add(@vardir, "/filenames")) - ) - - @filenames ||= "" - - # convert \n separated string to ycp list. - - @all_files = Builtins.splitstring(@filenames, "\n") - @all_files |= DEFAULT_FILENAMES - - @set_default = false - @combo_files = [] - - # check if default given as argument - - @filename = "" - if Ops.greater_than(Builtins.size(WFM.Args), 0) && - Ops.is_string?(WFM.Args(0)) - @filename = Convert.to_string(WFM.Args(0)) - if @filename != "" - @combo_files = [Item(Id(@filename), @filename, true)] - @set_default = true - end - end - - # the command line description map - @cmdline = { "id" => "view_anymsg" } - return CommandLine.Run(@cmdline) if @filename == "help" - - # build up ComboBox - - Builtins.foreach(@all_files) do |name| - # empty lines or lines starting with "#" are ignored - if name != "" && Builtins.substring(name, 0, 1) != "#" - # the default is either given via WFM::Args() -> filename != "" - # or by a filename starting with "*" - if Builtins.substring(name, 0, 1) == "*" - name = Builtins.substring(name, 1) # strip leading "*" - if name != @filename # do not add it twice - @combo_files = Builtins.add( - @combo_files, - Item(Id(name), name, !@set_default) - ) - end - if !@set_default - @filename = name if @filename == "" - @set_default = true - end - elsif name != @filename # do not add it twice - @combo_files = Builtins.add(@combo_files, Item(Id(name), name)) - end - end - end - - if !@set_default && @filename != "" - @all_files = Builtins.add(@all_files, Ops.add("*", @filename)) - @combo_files = Builtins.add( - @combo_files, - Item(Id(@filename), @filename) - ) - end - - # set up dialogue - - UI.OpenDialog( - Opt(:decorated, :defaultsize), - VBox( - HSpacing(70), # force width - HBox( - HSpacing(1.0), - ComboBox( - Id(:custom_file), - Opt(:editable, :notify, :hstretch), - "", - @combo_files - ), - HStretch() - ), - VSpacing(0.3), - VWeight( - 1, - HBox( - VSpacing(18), # force height - HSpacing(0.7), - LogView( - Id(:log), - "", - 3, # height - 0 - ), # number of lines to show - HSpacing(0.7) - ) - ), - VSpacing(0.3), - PushButton(Id(:ok), Label.OKButton), - VSpacing(0.3) - ) - ) - - @go_on = true - - # wait until user clicks "OK" - # check if ComboBox selected and change view accordingly - - while @go_on - - # read file content - file_content = SCR.Read(path(".target.string"), @filename) - - if file_content - # replace invalid byte sequences with Unicode "replacement character" - file_content.scrub!("�") - # remove ANSI color escape sequences - file_content.remove_ansi_sequences - # remove remaining ASCII control characters (ASCII 0-31 and 127 (DEL)) - # except new line (LF = 0xa) and carriage return (CR = 0xd) - file_content.tr!("\u0000-\u0009\u000b\u000c\u000e-\u001f\u007f", "") - else - file_content = _("File not found.") - end - - # Fill the LogView with file content - UI.ChangeWidget(Id(:log), :Value, file_content) - - heading = Builtins.sformat(_("System Log (%1)"), @filename) - UI.ChangeWidget(Id(:log), :Label, heading) - - # wait for user input - - @ret = Convert.to_symbol(UI.UserInput) - - # clicked "OK" -> exit - - if @ret == :ok - @go_on = false - elsif @ret == :cancel # close window - UI.CloseDialog - return true - elsif @ret == :custom_file - # adapt to combo box settings - - @new_file = Convert.to_string( - UI.QueryWidget(Id(:custom_file), :Value) - ) - @filename = @new_file if !@new_file.nil? - else - Builtins.y2milestone("bad UserInput (%1)", @ret) - end - end - - # write new list of filenames - - @new_files = [] - @set_default = false - - # re-build list to get new default correct - Builtins.foreach(@all_files) do |file| - if Builtins.substring(file, 0, 1) == "*" - old_default = Builtins.substring(file, 1) # strip leading "*" - if old_default == @filename # default unchanged - @new_files = Builtins.add(@new_files, file) - @set_default = true # new default - else - @new_files = Builtins.add(@new_files, old_default) - end - elsif file != "" - if file == @filename # mark new default - @new_files = Builtins.add(@new_files, Ops.add("*", @filename)) - @set_default = true - else - @new_files = Builtins.add(@new_files, file) - end - end - end - # if we don't have a default by now, it wasn't in the list before - # so add it here. - - if !@set_default && @filename != "" - @new_files = Builtins.add(@new_files, Ops.add("*", @filename)) - end - - @new_files = Builtins.toset(@new_files) - - # convert ycp list back to \n separated string - - @filenames = Ops.add(Builtins.mergestring(@new_files, "\n"), "\n") - - SCR.Write( - path(".target.string"), - Ops.add(@vardir, "/filenames"), - @filenames - ) - - UI.CloseDialog - true - end - end -end +require "yast2/clients/view_anymsg" Yast::ViewAnymsgClient.new.main diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb new file mode 100644 index 000000000..33855cac6 --- /dev/null +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -0,0 +1,276 @@ +# encoding: utf-8 + +# *************************************************************************** +# +# Copyright (c) 2002 - 2012 Novell, Inc. +# 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 Novell, Inc. +# +# To contact Novell about this file by physical or electronic mail, +# you may find current contact information at www.novell.com +# +# *************************************************************************** + +require "yast/core_ext" + +Yast.import "UI" +Yast.import "CommandLine" +Yast.import "Directory" +Yast.import "FileUtils" +Yast.import "Label" + +module Yast + # Reads a \n separated list of filenames from + # /var/lib/YaST2/filenames + # Lines starting with "#" are ignored (comments) + # A line starting with "*" is taken as the default filename, the "*" is stripped + # + # All files are listed in an editable combo box, where the user can + # easily switch between files and even add a new file + # + # At finish, the list of filenames is written back to + # /var/lib/YaST2/filenames + # adapting the default line (starting with "*") accordingly. + # + # The default is either given as WFM::Args(0) or is the file last viewed. + class ViewAnymsgClient < Client + using Yast::CoreExt::AnsiString + + # [String] Default list of log files + DEFAULT_FILENAMES = [ + "/var/log/boot.log", + "/var/log/messages", + "/var/log/YaST2/y2log" + ].freeze + + def main + textdomain "base" + + @vardir = Directory.vardir + + # Check if the filename list is present + if !FileUtils.Exists(Ops.add(@vardir, "/filenames")) + SCR.Execute( + path(".target.bash"), + Ops.add( + Ops.add( + Ops.add(Ops.add("/bin/cp ", Directory.ydatadir), "/filenames "), + @vardir + ), + "/filenames" + ) + ) + end + + # get filename list + @filenames = Convert.to_string( + SCR.Read(path(".target.string"), Ops.add(@vardir, "/filenames")) + ) + + @filenames ||= "" + + # convert \n separated string to ycp list. + + @all_files = Builtins.splitstring(@filenames, "\n") + @all_files |= DEFAULT_FILENAMES + + @set_default = false + @combo_files = [] + + # check if default given as argument + + @filename = "" + if Ops.greater_than(Builtins.size(WFM.Args), 0) && + Ops.is_string?(WFM.Args(0)) + @filename = Convert.to_string(WFM.Args(0)) + if @filename != "" + @combo_files = [Item(Id(@filename), @filename, true)] + @set_default = true + end + end + + # the command line description map + @cmdline = { "id" => "view_anymsg" } + return CommandLine.Run(@cmdline) if @filename == "help" + + # build up ComboBox + + Builtins.foreach(@all_files) do |name| + # empty lines or lines starting with "#" are ignored + if name != "" && Builtins.substring(name, 0, 1) != "#" + # the default is either given via WFM::Args() -> filename != "" + # or by a filename starting with "*" + if Builtins.substring(name, 0, 1) == "*" + name = Builtins.substring(name, 1) # strip leading "*" + if name != @filename # do not add it twice + @combo_files = Builtins.add( + @combo_files, + Item(Id(name), name, !@set_default) + ) + end + if !@set_default + @filename = name if @filename == "" + @set_default = true + end + elsif name != @filename # do not add it twice + @combo_files = Builtins.add(@combo_files, Item(Id(name), name)) + end + end + end + + if !@set_default && @filename != "" + @all_files = Builtins.add(@all_files, Ops.add("*", @filename)) + @combo_files = Builtins.add( + @combo_files, + Item(Id(@filename), @filename) + ) + end + + # set up dialogue + + UI.OpenDialog( + Opt(:decorated, :defaultsize), + VBox( + HSpacing(70), # force width + HBox( + HSpacing(1.0), + ComboBox( + Id(:custom_file), + Opt(:editable, :notify, :hstretch), + "", + @combo_files + ), + HStretch() + ), + VSpacing(0.3), + VWeight( + 1, + HBox( + VSpacing(18), # force height + HSpacing(0.7), + LogView( + Id(:log), + "", + 3, # height + 0 + ), # number of lines to show + HSpacing(0.7) + ) + ), + VSpacing(0.3), + PushButton(Id(:ok), Label.OKButton), + VSpacing(0.3) + ) + ) + + @go_on = true + + # wait until user clicks "OK" + # check if ComboBox selected and change view accordingly + + while @go_on + + # read file content + file_content = SCR.Read(path(".target.string"), @filename) + + if file_content + # replace invalid byte sequences with Unicode "replacement character" + file_content.scrub!("�") + # remove ANSI color escape sequences + file_content.remove_ansi_sequences + # remove remaining ASCII control characters (ASCII 0-31 and 127 (DEL)) + # except new line (LF = 0xa) and carriage return (CR = 0xd) + file_content.tr!("\u0000-\u0009\u000b\u000c\u000e-\u001f\u007f", "") + else + file_content = _("File not found.") + end + + # Fill the LogView with file content + UI.ChangeWidget(Id(:log), :Value, file_content) + + heading = Builtins.sformat(_("System Log (%1)"), @filename) + UI.ChangeWidget(Id(:log), :Label, heading) + + # wait for user input + + @ret = Convert.to_symbol(UI.UserInput) + + # clicked "OK" -> exit + + if @ret == :ok + @go_on = false + elsif @ret == :cancel # close window + UI.CloseDialog + return true + elsif @ret == :custom_file + # adapt to combo box settings + + @new_file = Convert.to_string( + UI.QueryWidget(Id(:custom_file), :Value) + ) + @filename = @new_file if !@new_file.nil? + else + Builtins.y2milestone("bad UserInput (%1)", @ret) + end + end + + # write new list of filenames + + @new_files = [] + @set_default = false + + # re-build list to get new default correct + Builtins.foreach(@all_files) do |file| + if Builtins.substring(file, 0, 1) == "*" + old_default = Builtins.substring(file, 1) # strip leading "*" + if old_default == @filename # default unchanged + @new_files = Builtins.add(@new_files, file) + @set_default = true # new default + else + @new_files = Builtins.add(@new_files, old_default) + end + elsif file != "" + if file == @filename # mark new default + @new_files = Builtins.add(@new_files, Ops.add("*", @filename)) + @set_default = true + else + @new_files = Builtins.add(@new_files, file) + end + end + end + # if we don't have a default by now, it wasn't in the list before + # so add it here. + + if !@set_default && @filename != "" + @new_files = Builtins.add(@new_files, Ops.add("*", @filename)) + end + + @new_files = Builtins.toset(@new_files) + + # convert ycp list back to \n separated string + + @filenames = Ops.add(Builtins.mergestring(@new_files, "\n"), "\n") + + SCR.Write( + path(".target.string"), + Ops.add(@vardir, "/filenames"), + @filenames + ) + + UI.CloseDialog + + true + end + end +end diff --git a/library/system/test/Makefile.am b/library/system/test/Makefile.am index 3b54c3128..58867fac5 100644 --- a/library/system/test/Makefile.am +++ b/library/system/test/Makefile.am @@ -1,4 +1,5 @@ TESTS = \ + clients/view_anymsg_test.rb \ execute_test.rb \ kernel_test.rb \ hw_detection_test.rb \ diff --git a/library/system/test/clients/view_anymsg_test.rb b/library/system/test/clients/view_anymsg_test.rb new file mode 100755 index 000000000..87b6695ef --- /dev/null +++ b/library/system/test/clients/view_anymsg_test.rb @@ -0,0 +1,176 @@ +#!/usr/bin/env rspec + +require_relative "../test_helper" + +require "yast2/clients/view_anymsg" + +describe Yast::ViewAnymsgClient do + describe "#main" do + before do + # UI mockup + allow(Yast::UI).to receive(:OpenDialog) + allow(Yast::UI).to receive(:ChangeWidget) + allow(Yast::UI).to receive(:QueryWidget) + allow(Yast::UI).to receive(:UserInput).and_return(:ok) + allow(Yast::UI).to receive(:CloseDialog) + + # SCR mock + allow(Yast::SCR).to receive(:Execute) + allow(Yast::SCR).to receive(:Read) + allow(Yast::SCR).to receive(:Write) + + # WFM mock + allow(Yast::WFM).to receive(:Args).and_return([]) + end + + it "returns true" do + expect(subject.main).to eq true + end + + context "filenames list does not exist yet" do + it "copies filenames list from ydata dir to var dir" do + allow(Yast::FileUtils).to receive(:Exists).and_return(false) + expect(Yast::SCR).to receive(:Execute).with( + path(".target.bash"), + "/bin/cp /usr/share/YaST2/data/filenames /var/lib/YaST2/filenames" + ) + + subject.main + end + end + + it "shows help text for command line if help parameter is passed" do + allow(Yast::WFM).to receive(:Args).with(no_args).and_return(["help"]) + allow(Yast::WFM).to receive(:Args).with(0).and_return("help") + + expect(Yast::CommandLine).to receive(:Run).and_return(true) + + subject.main + end + + context "filenames list" do + def combobox_items(&block) + expect(Yast::UI).to receive(:OpenDialog) do |opts, term| + items = term.nested_find do |i| + i.is_a?(::Array) && i.first.is_a?(Yast::Term) && i.first.value == :item + end + + expect(items).to(be_a(Array), "Not found ComboBox items #{term.inspect}") + + block.call(items) + end + end + + def expect_to_include(*filenames) + combobox_items do |items| + filenames.each do |path| + result = items.find do |i| + i.value == :item && i.params.include?(path) + end + + expect(result).to_not(eq(nil), "Not found path '#{path}' in Items - #{items.inspect}") + end + end + end + + it "reads filenames list from var dir" do + expect(Yast::SCR).to receive(:Read).with(path(".target.string"), "/var/lib/YaST2/filenames") + .and_return("") + + subject.main + end + + it "uses empty list if read failed" do + expect(Yast::SCR).to receive(:Read).with(path(".target.string"), "/var/lib/YaST2/filenames") + .and_return(nil) + + subject.main + end + + it "merge list with default files" do + expect_to_include(*Yast::ViewAnymsgClient::DEFAULT_FILENAMES) + + subject.main + end + + it "adds to ComboBox all file from filenames list" do + filenames = ["/tmp/test", "/tmp/lest"] + expect(Yast::SCR).to receive(:Read).with(path(".target.string"), "/var/lib/YaST2/filenames") + .and_return(filenames.join("\n")) + expect_to_include(*filenames) + + subject.main + end + + it "skips lines with # at the beginning" do + filenames = ["/tmp/test", "#/tmp/lest"] + + combobox_items do |items| + items.each do |item| + value = item.params[1] + expect(value).to_not match /lest/ + end + end + + expect(Yast::SCR).to receive(:Read).with(path(".target.string"), "/var/lib/YaST2/filenames") + .and_return(filenames.join("\n")) + + subject.main + end + + it "strips the leading * and mark item as default" do + filenames = ["/tmp/test", "*/tmp/lest"] + expect(Yast::SCR).to receive(:Read).with(path(".target.string"), "/var/lib/YaST2/filenames") + .and_return(filenames.join("\n")) + + combobox_items do |items| + res = items.find do |item| + item.params[1] == "/tmp/lest" + end + + expect(res).to_not(be_nil, "Not found /tmp/lest item in #{items.inspect}") + expect(res.params[2]).to(eq(true), "Item is not marked as default #{items.inspect}") + end + + subject.main + end + + it "writes new default to filenames list" do + allow(Yast::UI).to receive(:UserInput).and_return(:custom_file, :ok) + allow(Yast::UI).to receive(:QueryWidget).with(Id(:custom_file), :Value) + .and_return("/var/log/boot.log") + + expect(Yast::SCR).to receive(:Write) do |scr_path, filepath, content| + expect(scr_path).to eq path(".target.string") + expect(filepath).to eq "/var/lib/YaST2/filenames" + expect(content).to include("*/var/log/boot.log") + end + + subject.main + end + + it "writes new custom file to filenames list" do + allow(Yast::UI).to receive(:UserInput).and_return(:custom_file, :ok) + allow(Yast::UI).to receive(:QueryWidget).with(Id(:custom_file), :Value) + .and_return("/var/log/not_seen_before.log") + + expect(Yast::SCR).to receive(:Write) do |scr_path, filepath, content| + expect(scr_path).to eq path(".target.string") + expect(filepath).to eq "/var/lib/YaST2/filenames" + expect(content).to include("*/var/log/not_seen_before.log") + end + + subject.main + + end + + it "does not write anything when user click cancel" do + allow(Yast::UI).to receive(:UserInput).and_return(:cancel) + + expect(Yast::SCR).to_not receive(:Write) + + subject.main + end + end + end +end From be21fdf22dca68e4071c46978fbbbbd736984090 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 15:32:27 +0200 Subject: [PATCH 200/223] extract vardir variable --- .../system/src/lib/yast2/clients/view_anymsg.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 33855cac6..78327d640 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -57,16 +57,14 @@ class ViewAnymsgClient < Client def main textdomain "base" - @vardir = Directory.vardir - # Check if the filename list is present - if !FileUtils.Exists(Ops.add(@vardir, "/filenames")) + if !FileUtils.Exists(Ops.add(vardir, "/filenames")) SCR.Execute( path(".target.bash"), Ops.add( Ops.add( Ops.add(Ops.add("/bin/cp ", Directory.ydatadir), "/filenames "), - @vardir + vardir ), "/filenames" ) @@ -75,7 +73,7 @@ def main # get filename list @filenames = Convert.to_string( - SCR.Read(path(".target.string"), Ops.add(@vardir, "/filenames")) + SCR.Read(path(".target.string"), Ops.add(vardir, "/filenames")) ) @filenames ||= "" @@ -264,7 +262,7 @@ def main SCR.Write( path(".target.string"), - Ops.add(@vardir, "/filenames"), + Ops.add(vardir, "/filenames"), @filenames ) @@ -272,5 +270,11 @@ def main true end + + private + + def vardir + @vardir ||= Directory.vardir + end end end From 132167f030cdd180577efc2790a9c6259a145e68 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 15:35:55 +0200 Subject: [PATCH 201/223] extract filenames_path --- .../src/lib/yast2/clients/view_anymsg.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 78327d640..39e2aa982 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -58,22 +58,16 @@ def main textdomain "base" # Check if the filename list is present - if !FileUtils.Exists(Ops.add(vardir, "/filenames")) + if !FileUtils.Exists(filenames_path) SCR.Execute( path(".target.bash"), - Ops.add( - Ops.add( - Ops.add(Ops.add("/bin/cp ", Directory.ydatadir), "/filenames "), - vardir - ), - "/filenames" - ) + "/bin/cp #{::File.join(Directory.ydatadir, "filenames")} #{filenames_path}" ) end # get filename list @filenames = Convert.to_string( - SCR.Read(path(".target.string"), Ops.add(vardir, "/filenames")) + SCR.Read(path(".target.string"), filenames_path) ) @filenames ||= "" @@ -262,7 +256,7 @@ def main SCR.Write( path(".target.string"), - Ops.add(vardir, "/filenames"), + filenames_path, @filenames ) @@ -273,8 +267,8 @@ def main private - def vardir - @vardir ||= Directory.vardir + def filenames_path + @filenames_path ||= ::File.join(Directory.vardir, "filenames") end end end From 3fb10d6d5ab27b967feba70f9803b29852128fd1 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 15:39:48 +0200 Subject: [PATCH 202/223] early exit for CLI help --- library/system/src/lib/yast2/clients/view_anymsg.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 39e2aa982..84e1d031a 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -57,6 +57,10 @@ class ViewAnymsgClient < Client def main textdomain "base" + # the command line description map + return CommandLine.Run("id" => "view_anymsg") if WFM.Args.first == "help" + + # Check if the filename list is present if !FileUtils.Exists(filenames_path) SCR.Execute( @@ -92,10 +96,6 @@ def main end end - # the command line description map - @cmdline = { "id" => "view_anymsg" } - return CommandLine.Run(@cmdline) if @filename == "help" - # build up ComboBox Builtins.foreach(@all_files) do |name| From e8787e113d716d3302f8f2449c848446362aabf4 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 15:40:43 +0200 Subject: [PATCH 203/223] extract initial filenames creation --- .../src/lib/yast2/clients/view_anymsg.rb | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 84e1d031a..f1733fc1c 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -60,14 +60,8 @@ def main # the command line description map return CommandLine.Run("id" => "view_anymsg") if WFM.Args.first == "help" + ensure_filenames_exist - # Check if the filename list is present - if !FileUtils.Exists(filenames_path) - SCR.Execute( - path(".target.bash"), - "/bin/cp #{::File.join(Directory.ydatadir, "filenames")} #{filenames_path}" - ) - end # get filename list @filenames = Convert.to_string( @@ -270,5 +264,17 @@ def main def filenames_path @filenames_path ||= ::File.join(Directory.vardir, "filenames") end + + def ensure_filenames_exist + # Check if the filename list is present + if !FileUtils.Exists(filenames_path) + SCR.Execute( + path(".target.bash"), + "/bin/cp #{::File.join(Directory.ydatadir, "filenames")} #{filenames_path}" + ) + end + end + + end end From 275c87940f7a95c6a2f41fe0c1b9e54487c6fa0e Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 15:45:56 +0200 Subject: [PATCH 204/223] extract file content --- .../src/lib/yast2/clients/view_anymsg.rb | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index f1733fc1c..6a2abe1f8 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -166,24 +166,8 @@ def main # check if ComboBox selected and change view accordingly while @go_on - - # read file content - file_content = SCR.Read(path(".target.string"), @filename) - - if file_content - # replace invalid byte sequences with Unicode "replacement character" - file_content.scrub!("�") - # remove ANSI color escape sequences - file_content.remove_ansi_sequences - # remove remaining ASCII control characters (ASCII 0-31 and 127 (DEL)) - # except new line (LF = 0xa) and carriage return (CR = 0xd) - file_content.tr!("\u0000-\u0009\u000b\u000c\u000e-\u001f\u007f", "") - else - file_content = _("File not found.") - end - # Fill the LogView with file content - UI.ChangeWidget(Id(:log), :Value, file_content) + UI.ChangeWidget(Id(:log), :Value, file_content(@filename)) heading = Builtins.sformat(_("System Log (%1)"), @filename) UI.ChangeWidget(Id(:log), :Label, heading) @@ -275,6 +259,24 @@ def ensure_filenames_exist end end + def file_content(filename) + # read file content + result = SCR.Read(path(".target.string"), @filename) + + if result + # replace invalid byte sequences with Unicode "replacement character" + result.scrub!("�") + # remove ANSI color escape sequences + result.remove_ansi_sequences + # remove remaining ASCII control characters (ASCII 0-31 and 127 (DEL)) + # except new line (LF = 0xa) and carriage return (CR = 0xd) + result.tr!("\u0000-\u0009\u000b\u000c\u000e-\u001f\u007f", "") + else + result = _("File not found.") + end + + result + end end end From b62a1f5cc0ff2220307bd035187169348c87b77a Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 16:43:26 +0200 Subject: [PATCH 205/223] refactor handling of filenames file --- .../src/lib/yast2/clients/view_anymsg.rb | 200 ++++++++---------- 1 file changed, 93 insertions(+), 107 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 6a2abe1f8..275cd989d 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -60,71 +60,7 @@ def main # the command line description map return CommandLine.Run("id" => "view_anymsg") if WFM.Args.first == "help" - ensure_filenames_exist - - - # get filename list - @filenames = Convert.to_string( - SCR.Read(path(".target.string"), filenames_path) - ) - - @filenames ||= "" - - # convert \n separated string to ycp list. - - @all_files = Builtins.splitstring(@filenames, "\n") - @all_files |= DEFAULT_FILENAMES - - @set_default = false - @combo_files = [] - - # check if default given as argument - - @filename = "" - if Ops.greater_than(Builtins.size(WFM.Args), 0) && - Ops.is_string?(WFM.Args(0)) - @filename = Convert.to_string(WFM.Args(0)) - if @filename != "" - @combo_files = [Item(Id(@filename), @filename, true)] - @set_default = true - end - end - - # build up ComboBox - - Builtins.foreach(@all_files) do |name| - # empty lines or lines starting with "#" are ignored - if name != "" && Builtins.substring(name, 0, 1) != "#" - # the default is either given via WFM::Args() -> filename != "" - # or by a filename starting with "*" - if Builtins.substring(name, 0, 1) == "*" - name = Builtins.substring(name, 1) # strip leading "*" - if name != @filename # do not add it twice - @combo_files = Builtins.add( - @combo_files, - Item(Id(name), name, !@set_default) - ) - end - if !@set_default - @filename = name if @filename == "" - @set_default = true - end - elsif name != @filename # do not add it twice - @combo_files = Builtins.add(@combo_files, Item(Id(name), name)) - end - end - end - - if !@set_default && @filename != "" - @all_files = Builtins.add(@all_files, Ops.add("*", @filename)) - @combo_files = Builtins.add( - @combo_files, - Item(Id(@filename), @filename) - ) - end - # set up dialogue - UI.OpenDialog( Opt(:decorated, :defaultsize), VBox( @@ -135,7 +71,7 @@ def main Id(:custom_file), Opt(:editable, :notify, :hstretch), "", - @combo_files + combobox_items ), HStretch() ), @@ -167,9 +103,9 @@ def main while @go_on # Fill the LogView with file content - UI.ChangeWidget(Id(:log), :Value, file_content(@filename)) + UI.ChangeWidget(Id(:log), :Value, file_content(selected_filename)) - heading = Builtins.sformat(_("System Log (%1)"), @filename) + heading = Builtins.sformat(_("System Log (%1)"), selected_filename) UI.ChangeWidget(Id(:log), :Label, heading) # wait for user input @@ -186,65 +122,50 @@ def main elsif @ret == :custom_file # adapt to combo box settings - @new_file = Convert.to_string( + new_file = Convert.to_string( UI.QueryWidget(Id(:custom_file), :Value) ) - @filename = @new_file if !@new_file.nil? + self.selected_filename = new_file if !new_file.nil? else Builtins.y2milestone("bad UserInput (%1)", @ret) end end - # write new list of filenames - - @new_files = [] - @set_default = false + write_new_filenames + UI.CloseDialog - # re-build list to get new default correct - Builtins.foreach(@all_files) do |file| - if Builtins.substring(file, 0, 1) == "*" - old_default = Builtins.substring(file, 1) # strip leading "*" - if old_default == @filename # default unchanged - @new_files = Builtins.add(@new_files, file) - @set_default = true # new default - else - @new_files = Builtins.add(@new_files, old_default) - end - elsif file != "" - if file == @filename # mark new default - @new_files = Builtins.add(@new_files, Ops.add("*", @filename)) - @set_default = true - else - @new_files = Builtins.add(@new_files, file) - end - end - end - # if we don't have a default by now, it wasn't in the list before - # so add it here. + true + end - if !@set_default && @filename != "" - @new_files = Builtins.add(@new_files, Ops.add("*", @filename)) - end + private - @new_files = Builtins.toset(@new_files) + def write_new_filenames + result = [] - # convert ycp list back to \n separated string + to_write = (available_filenames + [selected_filename]).uniq - @filenames = Ops.add(Builtins.mergestring(@new_files, "\n"), "\n") + # re-build list to get new default correct + filenames_content.lines.each do |line| + line.strip! + result << line if line.empty? || line.start_with?("#") + + line = line[1..-1] if line.start_with?("*") + to_write.delete(line) # remember that we already write it + line = "*" + line if selected_filename == line + result << line + end + to_write.each do |line| + line = "*" + line if selected_filename == line + result << line + end SCR.Write( path(".target.string"), filenames_path, - @filenames + result.join("\n") ) - - UI.CloseDialog - - true end - private - def filenames_path @filenames_path ||= ::File.join(Directory.vardir, "filenames") end @@ -259,6 +180,14 @@ def ensure_filenames_exist end end + attr_writer :selected_filename + + def selected_filename + return @selected_filename if @selected_filename + + @selected_filename = default_filename + end + def file_content(filename) # read file content result = SCR.Read(path(".target.string"), @filename) @@ -278,5 +207,62 @@ def file_content(filename) result end + def filenames_list + @filenames_list ||= filenames_content.lines.each_with_object([]) do |line, result| + line.strip! + next if line.empty? + next if line.start_with?("#") + + line = line[1..-1] if line.start_with?("*") + result << line + end + end + + def available_filenames + return @available_filenames if @available_filenames + + result = filenames_list + DEFAULT_FILENAMES + [arg_filename] + @available_filenames = result.uniq.compact + end + + def arg_filename + arg = WFM.Args.first + if arg.is_a?(::String) && !arg.empty? + return arg + else + nil + end + end + + def filenames_content + return @filenames_content if @filenames_content + + ensure_filenames_exist + + # get filename list + @filenames_content = Convert.to_string( + SCR.Read(path(".target.string"), filenames_path) + ) + + @filenames_content ||= "" + end + + def default_filename + return @default_filename if @default_filename + + return @default_filename = arg_filename if arg_filename + + default_line = filenames_content.lines.find { |l| l.start_with?("*") } + + return @default_filename = available_filenames.first unless default_line + + @default_filename = default_line[1..-1].strip + end + + def combobox_items + available_filenames.map do |filename| + Item(Id(filename), filename, filename == default_filename) + end + end end end From efca4f9638e18a81c188e348972ad5e3dd325e7a Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 17:07:09 +0200 Subject: [PATCH 206/223] extract dialog content --- .../src/lib/yast2/clients/view_anymsg.rb | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 275cd989d..e170ba67c 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -63,37 +63,7 @@ def main # set up dialogue UI.OpenDialog( Opt(:decorated, :defaultsize), - VBox( - HSpacing(70), # force width - HBox( - HSpacing(1.0), - ComboBox( - Id(:custom_file), - Opt(:editable, :notify, :hstretch), - "", - combobox_items - ), - HStretch() - ), - VSpacing(0.3), - VWeight( - 1, - HBox( - VSpacing(18), # force height - HSpacing(0.7), - LogView( - Id(:log), - "", - 3, # height - 0 - ), # number of lines to show - HSpacing(0.7) - ) - ), - VSpacing(0.3), - PushButton(Id(:ok), Label.OKButton), - VSpacing(0.3) - ) + dialog_content ) @go_on = true @@ -139,6 +109,40 @@ def main private + def dialog_content + VBox( + HSpacing(70), # force width + HBox( + HSpacing(1.0), + ComboBox( + Id(:custom_file), + Opt(:editable, :notify, :hstretch), + "", + combobox_items + ), + HStretch() + ), + VSpacing(0.3), + VWeight( + 1, + HBox( + VSpacing(18), # force height + HSpacing(0.7), + LogView( + Id(:log), + "", + 3, # height + 0 + ), # number of lines to show + HSpacing(0.7) + ) + ), + VSpacing(0.3), + PushButton(Id(:ok), Label.OKButton), + VSpacing(0.3) + ) + end + def write_new_filenames result = [] From 457fe0a7f130873fe47508c63b8232a55eb21117 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 17:10:21 +0200 Subject: [PATCH 207/223] simplify event loop --- .../src/lib/yast2/clients/view_anymsg.rb | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index e170ba67c..b84538763 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -66,12 +66,11 @@ def main dialog_content ) - @go_on = true - # wait until user clicks "OK" # check if ComboBox selected and change view accordingly + res = nil - while @go_on + loop do # Fill the LogView with file content UI.ChangeWidget(Id(:log), :Value, file_content(selected_filename)) @@ -80,28 +79,20 @@ def main # wait for user input - @ret = Convert.to_symbol(UI.UserInput) - - # clicked "OK" -> exit + res = UI.UserInput - if @ret == :ok - @go_on = false - elsif @ret == :cancel # close window - UI.CloseDialog - return true - elsif @ret == :custom_file + case res + when :ok, :cancel then break + when :custom_file # adapt to combo box settings - - new_file = Convert.to_string( - UI.QueryWidget(Id(:custom_file), :Value) - ) + new_file = UI.QueryWidget(Id(:custom_file), :Value) self.selected_filename = new_file if !new_file.nil? else - Builtins.y2milestone("bad UserInput (%1)", @ret) + Builtins.y2milestone("bad UserInput (%1)", res) end end - write_new_filenames + write_new_filenames if res == :ok UI.CloseDialog true From 335218edd53253aa95a9b3087b1d00399734d4be Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 25 Oct 2018 17:33:19 +0200 Subject: [PATCH 208/223] implement switching to journal for empty logs (bsc#948729) --- .../src/lib/yast2/clients/view_anymsg.rb | 53 ++++++++++++++++++- .../system/test/clients/view_anymsg_test.rb | 2 + 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index b84538763..54147e894 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -24,11 +24,14 @@ require "yast/core_ext" +require "yast2/popup" + Yast.import "UI" Yast.import "CommandLine" Yast.import "Directory" Yast.import "FileUtils" Yast.import "Label" +Yast.import "Package" module Yast # Reads a \n separated list of filenames from @@ -77,8 +80,12 @@ def main heading = Builtins.sformat(_("System Log (%1)"), selected_filename) UI.ChangeWidget(Id(:log), :Label, heading) - # wait for user input + if start_journal? + res = :journal + break + end + # wait for user input res = UI.UserInput case res @@ -95,11 +102,53 @@ def main write_new_filenames if res == :ok UI.CloseDialog + Yast::WFM.CallFunction("journal") if res == :journal + true end private + def start_journal? + return false if [nil, 0, -1].include?(FileUtils.GetSize(selected_filename)) + + res = Yast2::Popup.show( + _( + "Selected log file does not exist or is empty.\n" \ + "Many system components now log into systemd journal.\n" \ + "Do you want to start YaST module for systemd journal?" + ), + buttons: :yes_no, + focus: :no + ) == :yes + + return false unless res + + if !Package.Installed("yast2-journal") + if !Package.Available("yast2-journal") + Yast2::Popup.show( + _( + "YaST2 journal module is not available. Please check your repositories." + ), + headline: :error + ) + return false + end + + if !Package.DoInstall("yast2-journal") + Yast2::Popup.show( + _( + "YaST2 journal module failed to install." + ), + headline: :error + ) + return false + end + end + + true + end + def dialog_content VBox( HSpacing(70), # force width @@ -185,7 +234,7 @@ def selected_filename def file_content(filename) # read file content - result = SCR.Read(path(".target.string"), @filename) + result = SCR.Read(path(".target.string"), filename) if result # replace invalid byte sequences with Unicode "replacement character" diff --git a/library/system/test/clients/view_anymsg_test.rb b/library/system/test/clients/view_anymsg_test.rb index 87b6695ef..cb983c360 100755 --- a/library/system/test/clients/view_anymsg_test.rb +++ b/library/system/test/clients/view_anymsg_test.rb @@ -21,6 +21,8 @@ # WFM mock allow(Yast::WFM).to receive(:Args).and_return([]) + + allow(Yast::FileUtils).to receive(:GetSize).and_return(1) end it "returns true" do From 6f2b719420dc57d387ab8388f9df184b1c2ccb18 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 11:37:37 +0200 Subject: [PATCH 209/223] make rubocop happy and add more tests --- .../src/lib/yast2/clients/view_anymsg.rb | 34 ++++--------- .../system/test/clients/view_anymsg_test.rb | 51 ++++++++++++++++++- 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 54147e894..fd5b6b6b0 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -1,4 +1,4 @@ -# encoding: utf-8 +# encoding: utf-8 # *************************************************************************** # @@ -110,7 +110,7 @@ def main private def start_journal? - return false if [nil, 0, -1].include?(FileUtils.GetSize(selected_filename)) + return false unless [nil, 0, -1].include?(FileUtils.GetSize(selected_filename)) res = Yast2::Popup.show( _( @@ -119,7 +119,7 @@ def start_journal? "Do you want to start YaST module for systemd journal?" ), buttons: :yes_no, - focus: :no + focus: :no ) == :yes return false unless res @@ -135,15 +135,7 @@ def start_journal? return false end - if !Package.DoInstall("yast2-journal") - Yast2::Popup.show( - _( - "YaST2 journal module failed to install." - ), - headline: :error - ) - return false - end + return Package.DoInstall("yast2-journal") end true @@ -216,12 +208,12 @@ def filenames_path def ensure_filenames_exist # Check if the filename list is present - if !FileUtils.Exists(filenames_path) - SCR.Execute( - path(".target.bash"), - "/bin/cp #{::File.join(Directory.ydatadir, "filenames")} #{filenames_path}" - ) - end + return if FileUtils.Exists(filenames_path) + + SCR.Execute( + path(".target.bash"), + "/bin/cp #{::File.join(Directory.ydatadir, "filenames")} #{filenames_path}" + ) end attr_writer :selected_filename @@ -271,11 +263,7 @@ def available_filenames def arg_filename arg = WFM.Args.first - if arg.is_a?(::String) && !arg.empty? - return arg - else - nil - end + return arg if arg.is_a?(::String) && !arg.empty? end def filenames_content diff --git a/library/system/test/clients/view_anymsg_test.rb b/library/system/test/clients/view_anymsg_test.rb index cb983c360..b9440166f 100755 --- a/library/system/test/clients/view_anymsg_test.rb +++ b/library/system/test/clients/view_anymsg_test.rb @@ -14,6 +14,8 @@ allow(Yast::UI).to receive(:UserInput).and_return(:ok) allow(Yast::UI).to receive(:CloseDialog) + allow(Yast2::Popup).to receive(:show) + # SCR mock allow(Yast::SCR).to receive(:Execute) allow(Yast::SCR).to receive(:Read) @@ -21,6 +23,7 @@ # WFM mock allow(Yast::WFM).to receive(:Args).and_return([]) + allow(Yast::WFM).to receive(:CallFunction) allow(Yast::FileUtils).to receive(:GetSize).and_return(1) end @@ -52,7 +55,7 @@ context "filenames list" do def combobox_items(&block) - expect(Yast::UI).to receive(:OpenDialog) do |opts, term| + expect(Yast::UI).to receive(:OpenDialog) do |_opts, term| items = term.nested_find do |i| i.is_a?(::Array) && i.first.is_a?(Yast::Term) && i.first.value == :item end @@ -110,7 +113,7 @@ def expect_to_include(*filenames) combobox_items do |items| items.each do |item| value = item.params[1] - expect(value).to_not match /lest/ + expect(value).to_not match(/lest/) end end @@ -174,5 +177,49 @@ def expect_to_include(*filenames) subject.main end end + + context "log file does not exist or is empty" do + before do + allow(Yast::FileUtils).to receive(:GetSize).and_return(-1) + allow(Yast::Package).to receive(:Installed).and_return(true) + allow(Yast2::Popup).to receive(:show).and_return(:yes) + end + + it "ask if open journal instead" do + expect(Yast2::Popup).to receive(:show).and_return(:no) + + subject.main + end + + it "checks if yast2-journal is installed if user answer yes" do + expect(Yast::Package).to receive(:Installed).and_return(true) + + subject.main + end + + it "installs yast2-journal if it is not yet installed" do + allow(Yast::Package).to receive(:Installed).and_return(false) + allow(Yast::Package).to receive(:Available).and_return(true) + expect(Yast::Package).to receive(:DoInstall).and_return(true) + + subject.main + end + + it "reports error if package is not available" do + allow(Yast::Package).to receive(:Installed).and_return(false) + expect(Yast::Package).to receive(:Available).and_return(false) + expect(Yast::Package).to_not receive(:DoInstall) + + expect(Yast2::Popup).to receive(:show).with(anything, headline: :error) + + subject.main + end + + it "switches to yast2-journal module if user want and all goes well" do + expect(Yast::WFM).to receive(:CallFunction).with("journal") + + subject.main + end + end end end From d3e688380e03f7354c70565d7d684badcb921df5 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 13:35:47 +0200 Subject: [PATCH 210/223] fix passing param for install --- library/system/src/lib/yast2/clients/view_anymsg.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index fd5b6b6b0..28d1b4849 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -135,7 +135,7 @@ def start_journal? return false end - return Package.DoInstall("yast2-journal") + return Package.DoInstall(["yast2-journal"]) end true From a6020ce09aa0cbcaacb68f5b4b49ac4f0e6977f2 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 13:39:38 +0200 Subject: [PATCH 211/223] ask before installing yast2-journal --- library/system/src/lib/yast2/clients/view_anymsg.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 28d1b4849..2cd0d71f0 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -135,6 +135,14 @@ def start_journal? return false end + return false if Yast2::Popup.show( + _( + "YaST2 journal module is not installed. Do you want to install it now?" + ), + buttons: :yes_no, + focus: :yes + ) == :no + return Package.DoInstall(["yast2-journal"]) end From a976c245d05d8e0f366f24a84539e614a0f12980 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 13:48:28 +0200 Subject: [PATCH 212/223] changes --- package/yast2.changes | 7 +++++++ package/yast2.spec | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/package/yast2.changes b/package/yast2.changes index 4d26ab870..cd7a74b06 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Oct 26 11:46:16 UTC 2018 - jreidinger@suse.com + +- view_anymsg: allow user to switch to yast2-journal if file does + not exist or is empty (bsc#948729) +- 4.1.32 + ------------------------------------------------------------------- Wed Oct 24 16:55:08 UTC 2018 - schubi@suse.de diff --git a/package/yast2.spec b/package/yast2.spec index d0d7419c8..34da1e559 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.31 +Version: 4.1.32 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only @@ -51,7 +51,7 @@ BuildRequires: yast2-testsuite # UI::.SetApplicationTitle BuildRequires: yast2-ycp-ui-bindings >= 3.2.0 # for the PackageExtractor tests, just make sure they are present, -# these should be installed in the default build anyway + these should be installed in the default build anyway BuildRequires: cpio BuildRequires: rpm From 376d7ec03474bcd848d669cc95ead1be1c9940bd Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 13:54:29 +0200 Subject: [PATCH 213/223] make rubocop happy --- library/system/src/lib/yast2/clients/view_anymsg.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 2cd0d71f0..6697b4b84 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -140,7 +140,7 @@ def start_journal? "YaST2 journal module is not installed. Do you want to install it now?" ), buttons: :yes_no, - focus: :yes + focus: :yes ) == :no return Package.DoInstall(["yast2-journal"]) From 1bec83e661d80c8b2324eaa6cc71b91f41a48bde Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 14:15:43 +0200 Subject: [PATCH 214/223] revert accidental delete --- package/yast2.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/yast2.spec b/package/yast2.spec index 34da1e559..73886aff5 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -51,7 +51,7 @@ BuildRequires: yast2-testsuite # UI::.SetApplicationTitle BuildRequires: yast2-ycp-ui-bindings >= 3.2.0 # for the PackageExtractor tests, just make sure they are present, - these should be installed in the default build anyway +# these should be installed in the default build anyway BuildRequires: cpio BuildRequires: rpm From 5c7df0376eeafa97a551f525848c113ebadc753f Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 14:37:03 +0200 Subject: [PATCH 215/223] use Package.Install --- .../src/lib/yast2/clients/view_anymsg.rb | 24 +------------------ .../system/test/clients/view_anymsg_test.rb | 24 +++---------------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 6697b4b84..5377a9dc3 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -124,29 +124,7 @@ def start_journal? return false unless res - if !Package.Installed("yast2-journal") - if !Package.Available("yast2-journal") - Yast2::Popup.show( - _( - "YaST2 journal module is not available. Please check your repositories." - ), - headline: :error - ) - return false - end - - return false if Yast2::Popup.show( - _( - "YaST2 journal module is not installed. Do you want to install it now?" - ), - buttons: :yes_no, - focus: :yes - ) == :no - - return Package.DoInstall(["yast2-journal"]) - end - - true + return Package.Install("yast2-journal") end def dialog_content diff --git a/library/system/test/clients/view_anymsg_test.rb b/library/system/test/clients/view_anymsg_test.rb index b9440166f..1c166bce0 100755 --- a/library/system/test/clients/view_anymsg_test.rb +++ b/library/system/test/clients/view_anymsg_test.rb @@ -181,7 +181,7 @@ def expect_to_include(*filenames) context "log file does not exist or is empty" do before do allow(Yast::FileUtils).to receive(:GetSize).and_return(-1) - allow(Yast::Package).to receive(:Installed).and_return(true) + allow(Yast::Package).to receive(:Install).and_return(true) allow(Yast2::Popup).to receive(:show).and_return(:yes) end @@ -191,26 +191,8 @@ def expect_to_include(*filenames) subject.main end - it "checks if yast2-journal is installed if user answer yes" do - expect(Yast::Package).to receive(:Installed).and_return(true) - - subject.main - end - - it "installs yast2-journal if it is not yet installed" do - allow(Yast::Package).to receive(:Installed).and_return(false) - allow(Yast::Package).to receive(:Available).and_return(true) - expect(Yast::Package).to receive(:DoInstall).and_return(true) - - subject.main - end - - it "reports error if package is not available" do - allow(Yast::Package).to receive(:Installed).and_return(false) - expect(Yast::Package).to receive(:Available).and_return(false) - expect(Yast::Package).to_not receive(:DoInstall) - - expect(Yast2::Popup).to receive(:show).with(anything, headline: :error) + it "ensures yast2-journal is installed" do + expect(Yast::Package).to receive(:Install).and_return(true) subject.main end From 7c06ac66c01feb6604ca73cb20965af0fa915ce9 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 26 Oct 2018 14:41:33 +0200 Subject: [PATCH 216/223] make rubocop happy --- library/system/src/lib/yast2/clients/view_anymsg.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/system/src/lib/yast2/clients/view_anymsg.rb b/library/system/src/lib/yast2/clients/view_anymsg.rb index 5377a9dc3..692836c87 100644 --- a/library/system/src/lib/yast2/clients/view_anymsg.rb +++ b/library/system/src/lib/yast2/clients/view_anymsg.rb @@ -124,7 +124,7 @@ def start_journal? return false unless res - return Package.Install("yast2-journal") + Package.Install("yast2-journal") end def dialog_content From 6a73b2a0255b3c5fe1ba569a5b42b9426f2bdfc5 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 6 Nov 2018 15:15:53 +0100 Subject: [PATCH 217/223] allows FHS path of system-roles --- library/control/src/modules/WorkflowManager.rb | 13 ++++++++++++- library/control/test/workflow_manager_test.rb | 7 +++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/library/control/src/modules/WorkflowManager.rb b/library/control/src/modules/WorkflowManager.rb index cbf4bca92..40c36a21d 100644 --- a/library/control/src/modules/WorkflowManager.rb +++ b/library/control/src/modules/WorkflowManager.rb @@ -439,7 +439,18 @@ def control_file(source) dir = addon_control_dir(src, cleanup: true) fetch_package(src, package, dir) - path = File.join(dir, "installation.xml") + # lets first try FHS compliant path (bsc#1114573) + # sadly no glob escaping - https://bugs.ruby-lang.org/issues/8258 + # but as we generate directory, it should be ok + files = Dir.glob("#{dir}/usr/share/system-roles/*.xml") + if files.size == 1 + path = files.first + elsif files.size > 1 + log.error "more then one file in system role #{files.inspect}" + path = files.first + else + path = File.join(dir, "installation.xml") + end return nil unless File.exist?(path) log.info("installation.xml path: #{path}") diff --git a/library/control/test/workflow_manager_test.rb b/library/control/test/workflow_manager_test.rb index d1efba15c..da373777b 100755 --- a/library/control/test/workflow_manager_test.rb +++ b/library/control/test/workflow_manager_test.rb @@ -396,6 +396,13 @@ # the returned path contains "/installation.xml" at the end expect(subject.control_file(repo_id)).to end_with("/installation.xml") end + + it "returns path leading to system-roles dir if it exists" do + allow(Dir).to receive(:glob).and_return(["/tmp/usr/share/system-roles/superyast.xml"]) + expect(File).to receive(:exist?).with("/tmp/usr/share/system-roles/superyast.xml").and_return(true) + + expect(subject.control_file(repo_id)).to eq "/tmp/usr/share/system-roles/superyast.xml" + end end describe "#addon_control_dir" do From f7b0ba077984ff02e6678c6b8846b0d893de5a3e Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 6 Nov 2018 15:16:01 +0100 Subject: [PATCH 218/223] changes --- package/yast2.changes | 9 ++++++++- package/yast2.spec | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/package/yast2.changes b/package/yast2.changes index cd7a74b06..f02e39f64 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Nov 6 14:14:04 UTC 2018 - jreidinger@suse.com + +- WorkflowManager: Allow system roles to live in + /usr/share/system-roles/*.xml (bsc#1108176) +- 4.1.33 + ------------------------------------------------------------------- Fri Oct 26 11:46:16 UTC 2018 - jreidinger@suse.com @@ -10,7 +17,7 @@ Wed Oct 24 16:55:08 UTC 2018 - schubi@suse.de - Added flag save_y2logs to control.xml file in order to save YaST logs at the end of installation (fate#325737) -- 4.1.31 +- 4.1.31 ------------------------------------------------------------------- Wed Oct 24 15:55:08 UTC 2018 - jreidinger@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 73886aff5..1fb2aed4a 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.32 +Version: 4.1.33 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From ea7fe501e8313ed7c582cb8cd9929c2ba67af2a6 Mon Sep 17 00:00:00 2001 From: Arvin Schnell Date: Fri, 16 Nov 2018 14:40:40 +0100 Subject: [PATCH 219/223] check return value of OpenDialog to prevent crash --- library/packages/src/modules/PackagesUI.rb | 2 +- package/yast2.changes | 6 ++++++ package/yast2.spec | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/library/packages/src/modules/PackagesUI.rb b/library/packages/src/modules/PackagesUI.rb index cc88057cc..5929126fb 100644 --- a/library/packages/src/modules/PackagesUI.rb +++ b/library/packages/src/modules/PackagesUI.rb @@ -312,7 +312,7 @@ def RunPackageSelector(options) widget_options ) - UI.OpenDialog( + raise "Opening package selector failed." if !UI.OpenDialog( Opt(:defaultsize), if !widget_options.empty? PackageSelector(Id(:packages), widget_options, "") diff --git a/package/yast2.changes b/package/yast2.changes index f02e39f64..fc6e4ee00 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Fri Nov 16 14:38:31 CET 2018 - aschnell@suse.com + +- check return value of OpenDialog to prevent crash (bsc#1115745) +- 4.1.34 + ------------------------------------------------------------------- Tue Nov 6 14:14:04 UTC 2018 - jreidinger@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 1fb2aed4a..e4ee4ea78 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.33 +Version: 4.1.34 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only From 96303690a69f6fe6aa1b37576f2a23c0f4f62af8 Mon Sep 17 00:00:00 2001 From: Arvin Schnell Date: Fri, 16 Nov 2018 15:03:17 +0100 Subject: [PATCH 220/223] mark exception text for translation --- library/packages/src/modules/PackagesUI.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/packages/src/modules/PackagesUI.rb b/library/packages/src/modules/PackagesUI.rb index 5929126fb..2775c494e 100644 --- a/library/packages/src/modules/PackagesUI.rb +++ b/library/packages/src/modules/PackagesUI.rb @@ -312,7 +312,8 @@ def RunPackageSelector(options) widget_options ) - raise "Opening package selector failed." if !UI.OpenDialog( + # exception text + raise _("Opening package selector failed.") if !UI.OpenDialog( Opt(:defaultsize), if !widget_options.empty? PackageSelector(Id(:packages), widget_options, "") From 9d0adb53f2e55564e8f50bbad9c9cbbc3e140d3e Mon Sep 17 00:00:00 2001 From: Stefan Hundhammer Date: Tue, 20 Nov 2018 16:15:53 +0100 Subject: [PATCH 221/223] Added new global value enable_local_users --- library/control/src/modules/ProductFeatures.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/library/control/src/modules/ProductFeatures.rb b/library/control/src/modules/ProductFeatures.rb index 6fd6e0e09..93f8ceb9e 100644 --- a/library/control/src/modules/ProductFeatures.rb +++ b/library/control/src/modules/ProductFeatures.rb @@ -69,6 +69,7 @@ def main "vendor_url" => "", "enable_clone" => false, "disable_os_prober" => false, + "enable_local_users" => true, # FATE #304865 "base_product_license_directory" => "/etc/YaST2/licenses/base/", "full_system_media_name" => "", From e9d6b8947c276a1bc6023b82876bf517b5921aed Mon Sep 17 00:00:00 2001 From: Stefan Hundhammer Date: Tue, 20 Nov 2018 16:16:24 +0100 Subject: [PATCH 222/223] Added ProductFeatures::GetBooleanFeatureWithFallback --- .../control/src/modules/ProductFeatures.rb | 22 ++ library/control/test/ProductFeatures_test.rb | 197 ++++++++++++------ 2 files changed, 156 insertions(+), 63 deletions(-) diff --git a/library/control/src/modules/ProductFeatures.rb b/library/control/src/modules/ProductFeatures.rb index 93f8ceb9e..caa1c09e3 100644 --- a/library/control/src/modules/ProductFeatures.rb +++ b/library/control/src/modules/ProductFeatures.rb @@ -242,6 +242,27 @@ def GetBooleanFeature(section, feature) Ops.is_string?(value) && Builtins.tolower(Convert.to_string(value)) == "yes" end + # Get value of a boolean feature with a fallback value. + # + # @note This is a stable API function + # @param [String] section string section of the feature + # @param [String] feature feature name + # @param [Boolean] fallback + # + # @return [Boolean] the feature value or fallback if not specified + def GetBooleanFeatureWithFallback(section, feature, fallback) + value = GetFeature(section, feature) + return fallback if value.nil? + return value if Ops.is_boolean?(value) + + if value.respond_to?(:downcase) + return true if ["yes", "true"].include?(value.downcase) + return false if ["no", "false"].include?(value.downcase) + end + + fallback + end + # Get value of a feature # @note This is a stable API function # @param [String] section string section of the feature @@ -359,6 +380,7 @@ def ClearOverlay publish function: :InitIfNeeded, type: "void ()" publish function: :GetFeature, type: "any (string, string)" publish function: :GetBooleanFeature, type: "boolean (string, string)" + publish function: :GetBooleanFeatureWithFallback, type: "boolean (string, string, boolean)" publish function: :GetIntegerFeature, type: "integer (string, string)" publish function: :SetFeature, type: "void (string, string, any)" publish function: :SetStringFeature, type: "void (string, string, string)" diff --git a/library/control/test/ProductFeatures_test.rb b/library/control/test/ProductFeatures_test.rb index e2a672535..9f7e90212 100755 --- a/library/control/test/ProductFeatures_test.rb +++ b/library/control/test/ProductFeatures_test.rb @@ -7,84 +7,155 @@ describe Yast::ProductFeatures do subject { Yast::ProductFeatures } - before do - # ensure no overlay is active - subject.ClearOverlay - end + context "With simple features" do - let(:original_features) do - { - "globals" => { - "keyboard " => "Hammond", - "flags" => ["Uruguay", "Bhutan"] - }, - "partitioning" => { - "open_space" => false + let(:simple_features) do + { + "globals" => { + "enable_true" => true, + "enable_false" => false, + "enable_yes" => "yes", + "enable_no" => "no", + "enable_xy" => "xy", + "enable_empty" => "" + # "enable_missing" => nil + } } - } - end + end - let(:overlay_features) do - { - "globals" => { - "flags" => ["Namibia"] - }, - "software" => { - "packages" => ["tangut-fonts"] - } - } - end + before do + subject.Import(simple_features) + end - let(:resulting_features) do - { - "globals" => { - "keyboard " => "Hammond", - "flags" => ["Namibia"] - }, - "partitioning" => { - "open_space" => false - }, - "software" => { - "packages" => ["tangut-fonts"] - } - } - end + describe ".GetBooleanFeature" do + it "gets simple boolean values" do + expect(subject.GetBooleanFeature("globals", "enable_true")).to be true + expect(subject.GetBooleanFeature("globals", "enable_false")).to be false + end - describe ".SetOverlay" do - it "overrides desired values and keeps other values" do - subject.Import(original_features) - subject.SetOverlay(overlay_features) - expect(subject.Export).to eq(resulting_features) + it "understands 'yes'" do + expect(subject.GetBooleanFeature("globals", "enable_yes")).to be true + end + + it "falls back to 'false' for arbitrary texts" do + expect(subject.GetBooleanFeature("globals", "enable_xy")).to be false + end + + it "falls back to 'false' for empty strings" do + expect(subject.GetBooleanFeature("globals", "enable_empty")).to be false + end + + it "falls back to 'false' for missing values" do + expect(subject.GetBooleanFeature("globals", "enable_missing")).to be false + end end - it "raises RuntimeError if called twice without ClearOverlay meanwhile" do - subject.Import(original_features) - subject.SetOverlay(overlay_features) - expect { subject.SetOverlay(overlay_features) }.to raise_error(RuntimeError) + describe ".GetBooleanFeatureWithFallback" do + it "gets simple boolean values" do + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_true", false)).to be true + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_false", true)).to be false + end + + it "understands 'yes' and 'no'" do + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_yes", false)).to be true + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_no", true)).to be false + end + + it "uses the fallback for arbitrary texts" do + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_xy", true)).to be true + end + + it "uses the fallback for empty strings" do + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_empty", true)).to be true + end + + it "uses the fallback for missing values" do + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_missing", true)).to be true + expect(subject.GetBooleanFeatureWithFallback("globals", "enable_missing", false)).to be false + end end end - describe ".ClearOverlay" do - it "restores the original state" do - subject.Import(original_features) - subject.SetOverlay(overlay_features) + context "With overlays" do + before do + # ensure no overlay is active subject.ClearOverlay - expect(subject.Export).to eq(original_features) end - it "does nothing in second consequent call" do - subject.Import(original_features) - subject.SetOverlay(overlay_features) - subject.ClearOverlay - subject.SetFeature("globals", "keyboard", "test") - subject.ClearOverlay - expect(subject.Export).to_not eq(original_features) + let(:original_features) do + { + "globals" => { + "keyboard " => "Hammond", + "flags" => ["Uruguay", "Bhutan"] + }, + "partitioning" => { + "open_space" => false + } + } end - it "keeps the original state if nothing was overlaid" do - subject.Import(original_features) - subject.ClearOverlay - expect(subject.Export).to eq(original_features) + let(:overlay_features) do + { + "globals" => { + "flags" => ["Namibia"] + }, + "software" => { + "packages" => ["tangut-fonts"] + } + } + end + + let(:resulting_features) do + { + "globals" => { + "keyboard " => "Hammond", + "flags" => ["Namibia"] + }, + "partitioning" => { + "open_space" => false + }, + "software" => { + "packages" => ["tangut-fonts"] + } + } + end + + describe ".SetOverlay" do + it "overrides desired values and keeps other values" do + subject.Import(original_features) + subject.SetOverlay(overlay_features) + expect(subject.Export).to eq(resulting_features) + end + + it "raises RuntimeError if called twice without ClearOverlay meanwhile" do + subject.Import(original_features) + subject.SetOverlay(overlay_features) + expect { subject.SetOverlay(overlay_features) }.to raise_error(RuntimeError) + end + end + + describe ".ClearOverlay" do + it "restores the original state" do + subject.Import(original_features) + subject.SetOverlay(overlay_features) + subject.ClearOverlay + expect(subject.Export).to eq(original_features) + end + + it "does nothing in second consequent call" do + subject.Import(original_features) + subject.SetOverlay(overlay_features) + subject.ClearOverlay + subject.SetFeature("globals", "keyboard", "test") + subject.ClearOverlay + expect(subject.Export).to_not eq(original_features) + end + + it "keeps the original state if nothing was overlaid" do + subject.Import(original_features) + subject.ClearOverlay + expect(subject.Export).to eq(original_features) + end end end end From b7d7bfd863b52ff5cd39ca9d438554ea83bea3b9 Mon Sep 17 00:00:00 2001 From: Stefan Hundhammer Date: Wed, 21 Nov 2018 18:27:56 +0100 Subject: [PATCH 223/223] Change log and version bump --- package/yast2.changes | 7 +++++++ package/yast2.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2.changes b/package/yast2.changes index fc6e4ee00..ed66d189b 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Nov 21 17:23:00 UTC 2018 - Stefan Hundhammer + +- Added global parameter enable_local_users (Fate#326447) +- Added ProductFeatures::GetBooleanFeatureWithFallback +- 4.1.35 + ------------------------------------------------------------------- Fri Nov 16 14:38:31 CET 2018 - aschnell@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index e4ee4ea78..5d1bbb994 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 4.1.34 +Version: 4.1.35 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0-only