Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package salt for openSUSE:Factory checked in 
at 2021-09-20 23:32:14
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/salt (Old)
 and      /work/SRC/openSUSE:Factory/.salt.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "salt"

Mon Sep 20 23:32:14 2021 rev:118 rq:919452 version:3002.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/salt/salt.changes        2021-04-22 
18:03:37.474475050 +0200
+++ /work/SRC/openSUSE:Factory/.salt.new.1899/salt.changes      2021-09-20 
23:33:00.271162557 +0200
@@ -1,0 +2,184 @@
+Wed Sep 15 11:18:58 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Exclude the full path of a download URL to prevent injection of
+  malicious code (bsc#1190265) (CVE-2021-21996)
+
+- Added:
+  * exclude-the-full-path-of-a-download-url-to-prevent-i.patch
+
+-------------------------------------------------------------------
+Tue Aug 31 11:28:13 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com>
+
+- Fix wrong relative paths resolution with Jinja renderer when importing 
subdirectories
+
+- Added:
+  * templates-move-the-globals-up-to-the-environment-jin.patch
+
+-------------------------------------------------------------------
+Thu Aug 19 14:41:12 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com>
+
+- Don't pass shell="/sbin/nologin" to onlyif/unless checks (bsc#1188259)
+- Add missing aarch64 to rpm package architectures
+- Backport of upstream PR#59492
+
+- Added:
+  * backport-of-upstream-pr59492-to-3002.2-404.patch
+  * don-t-use-shell-sbin-nologin-in-requisites.patch
+  * add-missing-aarch64-to-rpm-package-architectures-405.patch
+
+-------------------------------------------------------------------
+Wed Aug 11 12:22:24 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Fix failing unit test for systemd
+- Fix error handling in openscap module (bsc#1188647)
+- Better handling of bad public keys from minions (bsc#1189040)
+
+- Added:
+  * better-handling-of-bad-public-keys-from-minions-bsc-.patch
+  * fix-error-handling-in-openscap-module-bsc-1188647-40.patch
+  * fix-failing-unit-tests-for-systemd.patch
+
+-------------------------------------------------------------------
+Tue Aug 10 12:59:25 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Define license macro as doc in spec file if not existing
+- Add standalone formulas configuration for salt minion and remove salt-master 
requirement (bsc#1168327)
+
+-------------------------------------------------------------------
+Fri Jul 16 15:35:10 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Do noop for services states when running systemd in offline mode 
(bsc#1187787)
+- transactional_updates: do not execute states in parallel but use a queue 
(bsc#1188170)
+
+- Added:
+  * do-noop-for-services-states-when-running-systemd-in-.patch
+
+-------------------------------------------------------------------
+Thu Jul  8 08:06:40 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Handle "master tops" data when states are applied by "transactional_update" 
(bsc#1187787)
+- Enhance openscap module: add "xccdf_eval" call
+
+- Added:
+  * enhance-openscap-module-add-xccdf_eval-call-386.patch
+  * handle-master-tops-data-when-states-are-applied-by-t.patch
+
+-------------------------------------------------------------------
+Tue Jul  6 08:00:23 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com>
+
+- virt: pass emulator when getting domain capabilities from libvirt
+- Adding preliminary support for Rocky Linux
+- Implementation of held/unheld functions for state pkg (bsc#1187813)
+
+- Added:
+  * implementation-of-held-unheld-functions-for-state-pk.patch
+  * adding-preliminary-support-for-rocky.-59682-391.patch
+  * virt-pass-emulator-when-getting-domain-capabilities-.patch
+
+-------------------------------------------------------------------
+Fri Jun 25 11:54:13 UTC 2021 - Alexander Graul <alexander.gr...@suse.com>
+
+- Replace deprecated Thread.isAlive() with Thread.is_alive()
+
+- Added:
+  * backport-thread.is_alive-fix-390.patch
+
+-------------------------------------------------------------------
+Thu Jun 24 12:41:03 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com>
+
+- Fix exception in yumpkg.remove for not installed package
+- Fix save for iptables state module (bsc#1185131)
+
+- Added:
+  * fix-exception-in-yumpkg.remove-for-not-installed-pac.patch
+  * fix-save-for-iptables-state-module-bsc-1185131-372.patch
+
+-------------------------------------------------------------------
+Thu Jun 24 09:44:36 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- virt: use /dev/kvm to detect KVM
+
+- Added:
+  * virt-use-dev-kvm-to-detect-kvm-383.patch
+
+-------------------------------------------------------------------
+Thu Jun 24 08:41:31 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- zypperpkg: improve logic for handling vendorchange flags
+
+- Added:
+  * move-vendor-change-logic-to-zypper-class-355.patch
+
+-------------------------------------------------------------------
+Mon Jun 21 14:57:02 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Add bundled provides for tornado to the spec file
+- Enhance logging when inotify beacon is missing pyinotify (bsc#1186310)
+- Add "python3-pyinotify" as a recommended package for Salt in SUSE/OpenSUSE 
distros
+
+- Added:
+  * enhance-logging-when-inotify-beacon-is-missing-pyino.patch
+
+-------------------------------------------------------------------
+Fri Jun  4 09:00:07 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Fix tmpfiles.d configuration for salt to not use legacy paths (bsc#1173103)
+
+-------------------------------------------------------------------
+Tue Jun  1 12:05:20 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Check if dpkgnotify is executable (bsc#1186674)
+
+- Added:
+  * check-if-dpkgnotify-is-executable-bsc-1186674-376.patch
+
+-------------------------------------------------------------------
+Fri May 21 15:01:10 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Detect Python version to use inside container (bsc#1167586) (bsc#1164192)
+- Handle volumes on stopped pools in virt.vm_info (bsc#1186287)
+- Drop support for Python2. Obsoletes "python2-salt" package
+
+- Added:
+  * handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch
+  * figure-out-python-interpreter-to-use-inside-containe.patch
+
+-------------------------------------------------------------------
+Mon May 10 14:45:26 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- grains.extra: support old non-intel kernels (bsc#1180650)
+- Fix missing minion returns in batch mode (bsc#1184659)
+
+- Added:
+  * fix-missing-minion-returns-in-batch-mode-360.patch
+  * grains.extra-support-old-non-intel-kernels-bsc-11806.patch
+
+-------------------------------------------------------------------
+Tue May  4 13:44:13 UTC 2021 - Jochen Breuer <jbre...@suse.de>
+
+- Parsing Epoch out of version provided during pkg remove (bsc#1173692)
+
+- Added:
+  * parsing-epoch-out-of-version-provided-during-pkg-rem.patch
+
+-------------------------------------------------------------------
+Tue Apr 27 15:02:30 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Fix issue parsing errors in ansiblegate state module
+
+- Added:
+  * fix-issue-parsing-errors-in-ansiblegate-state-module.patch
+
+-------------------------------------------------------------------
+Tue Apr 27 12:27:17 UTC 2021 - Pablo Su??rez Hern??ndez 
<pablo.suarezhernan...@suse.com>
+
+- Prevent command injection in the snapper module (bsc#1185281) 
(CVE-2021-31607)
+- transactional_update: detect recursion in the executor
+- Add subpackage salt-transactional-update (jsc#SLE-18028)
+- Remove duplicate directories from specfile
+
+- Added:
+  * transactional_update-detect-recursion-in-the-executo.patch
+  * prevent-command-injection-in-the-snapper-module-bsc-.patch
+
+-------------------------------------------------------------------
@@ -4 +188 @@
-- Improvements on "ansiblegate" module:
+- Improvements on "ansiblegate" module (bsc#1185092):
@@ -223 +407 @@
-- Update to Salt release version 3002.2
+- Update to Salt release version 3002.2 (jsc#ECO-3212) (jsc#SLE-18033)

New:
----
  add-missing-aarch64-to-rpm-package-architectures-405.patch
  adding-preliminary-support-for-rocky.-59682-391.patch
  backport-of-upstream-pr59492-to-3002.2-404.patch
  backport-thread.is_alive-fix-390.patch
  better-handling-of-bad-public-keys-from-minions-bsc-.patch
  check-if-dpkgnotify-is-executable-bsc-1186674-376.patch
  do-noop-for-services-states-when-running-systemd-in-.patch
  don-t-use-shell-sbin-nologin-in-requisites.patch
  enhance-logging-when-inotify-beacon-is-missing-pyino.patch
  enhance-openscap-module-add-xccdf_eval-call-386.patch
  exclude-the-full-path-of-a-download-url-to-prevent-i.patch
  figure-out-python-interpreter-to-use-inside-containe.patch
  fix-error-handling-in-openscap-module-bsc-1188647-40.patch
  fix-exception-in-yumpkg.remove-for-not-installed-pac.patch
  fix-failing-unit-tests-for-systemd.patch
  fix-issue-parsing-errors-in-ansiblegate-state-module.patch
  fix-missing-minion-returns-in-batch-mode-360.patch
  fix-save-for-iptables-state-module-bsc-1185131-372.patch
  grains.extra-support-old-non-intel-kernels-bsc-11806.patch
  handle-master-tops-data-when-states-are-applied-by-t.patch
  handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch
  implementation-of-held-unheld-functions-for-state-pk.patch
  move-vendor-change-logic-to-zypper-class-355.patch
  parsing-epoch-out-of-version-provided-during-pkg-rem.patch
  prevent-command-injection-in-the-snapper-module-bsc-.patch
  templates-move-the-globals-up-to-the-environment-jin.patch
  transactional_update-detect-recursion-in-the-executo.patch
  transactional_update.conf
  virt-pass-emulator-when-getting-domain-capabilities-.patch
  virt-use-dev-kvm-to-detect-kvm-383.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ salt.spec ++++++
--- /var/tmp/diff_new_pack.WWBdJC/_old  2021-09-20 23:33:02.823165710 +0200
+++ /var/tmp/diff_new_pack.WWBdJC/_new  2021-09-20 23:33:02.827165715 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package salt
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -48,6 +48,7 @@
 Source3:        html.tar.bz2
 Source4:        update-documentation.sh
 Source5:        travis.yml
+Source6:        transactional_update.conf
 
 Patch1:         run-salt-master-as-dedicated-salt-user.patch
 Patch2:         run-salt-api-as-user-salt-bsc-1064520.patch
@@ -396,6 +397,66 @@
 Patch167:     regression-fix-of-salt-ssh-on-processing-targets-353.patch
 # PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60056
 Patch168:     improvements-on-ansiblegate-module-354.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520
+Patch169:       transactional_update-detect-recursion-in-the-executo.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59648
+Patch170:       prevent-command-injection-in-the-snapper-module-bsc-.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60056
+Patch171:       fix-issue-parsing-errors-in-ansiblegate-state-module.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/376
+Patch172:       check-if-dpkgnotify-is-executable-bsc-1186674-376.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/57881
+Patch173:       parsing-epoch-out-of-version-provided-during-pkg-rem.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58503
+Patch174:       fix-missing-minion-returns-in-batch-mode-360.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520
+Patch175:       grains.extra-support-old-non-intel-kernels-bsc-11806.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60133
+Patch176:       handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60229
+Patch177:       figure-out-python-interpreter-to-use-inside-containe.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60402
+Patch178:       enhance-logging-when-inotify-beacon-is-missing-pyino.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60421
+Patch179:       move-vendor-change-logic-to-zypper-class-355.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60420
+Patch180:       virt-use-dev-kvm-to-detect-kvm-383.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60358
+Patch181:       fix-save-for-iptables-state-module-bsc-1185131-372.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60356
+Patch182:       fix-exception-in-yumpkg.remove-for-not-installed-pac.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59535
+Patch183:       backport-thread.is_alive-fix-390.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/56935
+#                     https://github.com/saltstack/salt/pull/60432
+Patch184:       implementation-of-held-unheld-functions-for-state-pk.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59682
+Patch185:       adding-preliminary-support-for-rocky.-59682-391.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60492
+Patch186:       virt-pass-emulator-when-getting-domain-capabilities-.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59756
+Patch187:       enhance-openscap-module-add-xccdf_eval-call-386.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520
+Patch188:       handle-master-tops-data-when-states-are-applied-by-t.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520
+Patch189:       do-noop-for-services-states-when-running-systemd-in-.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60662
+#                     https://github.com/saltstack/salt/pull/60688
+Patch190:       better-handling-of-bad-public-keys-from-minions-bsc-.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59756
+Patch191:       fix-error-handling-in-openscap-module-bsc-1188647-40.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520
+Patch192:       fix-failing-unit-tests-for-systemd.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59492
+Patch193:       backport-of-upstream-pr59492-to-3002.2-404.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60650
+Patch194:       add-missing-aarch64-to-rpm-package-architectures-405.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/413
+Patch195:       don-t-use-shell-sbin-nologin-in-requisites.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60811
+Patch196:       templates-move-the-globals-up-to-the-environment-jin.patch
+# PATCH-FIX_UPSTREAM: 
https://github.com/saltstack/salt/commit/0b75ba190fda9c04cc026ad1aa4a6d572f40349b
+Patch197:       exclude-the-full-path-of-a-download-url-to-prevent-i.patch
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildRequires:  logrotate
@@ -404,6 +465,7 @@
 %endif
 
 Requires:       python3-%{name} = %{version}-%{release}
+Obsoletes:      python2-%{name}
 
 Requires(pre):  %{_sbindir}/groupadd
 Requires(pre):  %{_sbindir}/useradd
@@ -570,8 +632,11 @@
 Requires:       python3-xml
 Suggests:       python3-Mako
 Recommends:     python3-netaddr
+Recommends:     python3-pyinotify
 %endif
 
+Provides:       bundled(python3-tornado) = 4.5.3
+
 %description -n python3-salt
 Python3 specific files for salt
 
@@ -648,6 +713,9 @@
 Summary:        The client component for Saltstack
 Group:          System/Management
 Requires:       %{name} = %{version}-%{release}
+%if 0%{?suse_version} > 1500 || 0%{?sle_version} > 150000
+Requires:       (%{name}-transactional-update = %{version}-%{release} if 
read-only-root-fs)
+%endif
 
 %if %{with systemd}
 %{?systemd_requires}
@@ -778,19 +846,30 @@
 %package standalone-formulas-configuration
 Summary:        Standalone Salt configuration to make the packaged formulas 
available for the Salt master
 Group:          System/Management
-Requires:       %{name} = %{version}-%{release}
-Requires:       %{name}-master = %{version}-%{release}
+Requires:       %{name}
 Provides:       salt-formulas-configuration
 Conflicts:      otherproviders(salt-formulas-configuration)
 
 %description standalone-formulas-configuration
 This package adds the standalone configuration for the Salt master in order to 
make the packaged Salt formulas available on the Salt master
 
+%package transactional-update
+Summary:        Transactional update executor configuration
+Group:          System/Management
+Requires:       %{name} = %{version}-%{release}
+Requires:       %{name}-minion = %{version}-%{release}
+
+%description transactional-update
+For transactional systems, like MicroOS, Salt can operate
+transparently if the executor "transactional-update" is registered in
+list of active executors.  This package add the configuration file.
+
 
 %prep
 %setup -q -n salt-%{version}-suse
 cp %{S:1} .
 cp %{S:5} ./.travis.yml
+cp %{S:6} .
 %patch1 -p1
 %patch2 -p1
 %patch3 -p1
@@ -959,6 +1038,35 @@
 %patch166 -p1
 %patch167 -p1
 %patch168 -p1
+%patch169 -p1
+%patch170 -p1
+%patch171 -p1
+%patch172 -p1
+%patch173 -p1
+%patch174 -p1
+%patch175 -p1
+%patch176 -p1
+%patch177 -p1
+%patch178 -p1
+%patch179 -p1
+%patch180 -p1
+%patch181 -p1
+%patch182 -p1
+%patch183 -p1
+%patch184 -p1
+%patch185 -p1
+%patch186 -p1
+%patch187 -p1
+%patch188 -p1
+%patch189 -p1
+%patch190 -p1
+%patch191 -p1
+%patch192 -p1
+%patch193 -p1
+%patch194 -p1
+%patch195 -p1
+%patch196 -p1
+%patch197 -p1
 
 %build
 # Putting /usr/bin at the front of $PATH is needed for RHEL/RES 7. Without this
@@ -997,16 +1105,7 @@
 done
 
 ## create missing directories
-install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/master.d
-install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/minion.d
-install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.maps.d
-install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.profiles.d
-install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.providers.d
-install -Dd -m 0750 %{buildroot}%{_localstatedir}/log/salt
-install -Dd -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d/
-install -Dd -m 0755 %{buildroot}%{_sbindir}
-install -Dd -m 0750 %{buildroot}%{_localstatedir}/log/salt
-install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/minion/extmod
+install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/cloud
 install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master
 install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/jobs
 install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/proc
@@ -1014,12 +1113,8 @@
 install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/roots
 install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/syndics
 install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/tokens
-install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/cloud
-install -Dd -m 0750 %{buildroot}/var/lib/salt
-install -Dd -m 0750 %{buildroot}/srv/salt
-install -Dd -m 0750 %{buildroot}/srv/pillar
-install -Dd -m 0750 %{buildroot}/srv/spm
-install -Dd -m 0755 %{buildroot}%{_docdir}/salt
+install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/minion/extmod
+install -Dd -m 0750 %{buildroot}%{_localstatedir}/log/salt
 install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/
 install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.maps.d
 install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.profiles.d
@@ -1034,6 +1129,13 @@
 install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/pki/master/minions_pre
 install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/pki/master/minions_rejected
 install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/pki/minion
+install -Dd -m 0750 %{buildroot}/srv/pillar
+install -Dd -m 0750 %{buildroot}/srv/salt
+install -Dd -m 0750 %{buildroot}/srv/spm
+install -Dd -m 0750 %{buildroot}/var/lib/salt
+install -Dd -m 0755 %{buildroot}%{_docdir}/salt
+install -Dd -m 0755 %{buildroot}%{_sbindir}
+install -Dd -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d/
 
 # Install salt-support profiles
 install -Dpm 0644 salt/cli/support/profiles/* 
%{buildroot}%{python3_sitelib}/salt/cli/support/profiles
@@ -1097,6 +1199,7 @@
 install -Dpm 0640 conf/cloud %{buildroot}%{_sysconfdir}/salt/cloud
 install -Dpm 0640 conf/cloud.profiles 
%{buildroot}%{_sysconfdir}/salt/cloud.profiles
 install -Dpm 0640 conf/cloud.providers 
%{buildroot}%{_sysconfdir}/salt/cloud.providers
+install -Dpm 0640 transactional_update.conf 
%{buildroot}%{_sysconfdir}/salt/minion.d/transactional_update.conf
 #
 ## install logrotate file (for RHEL6 we use without sudo)
 %if 0%{?rhel} > 6 || 0%{?suse_version}
@@ -1128,6 +1231,7 @@
 install -Dd -m 0750 %{buildroot}%{_prefix}/share/salt-formulas/states
 install -Dd -m 0750 %{buildroot}%{_prefix}/share/salt-formulas/metadata
 install -Dpm 0640 conf/suse/standalone-formulas-configuration.conf 
%{buildroot}%{_sysconfdir}/salt/master.d
+install -Dpm 0640 conf/suse/standalone-formulas-configuration.conf 
%{buildroot}%{_sysconfdir}/salt/minion.d
 
 %if 0%{?suse_version} > 1020
 %fdupes %{buildroot}%{_docdir}
@@ -1580,12 +1684,9 @@
 %{_mandir}/man1/salt-call.1.gz
 %{_mandir}/man1/spm.1.gz
 %config(noreplace) %{_sysconfdir}/logrotate.d/salt
-%if 0%{?suse_version} < 1500
-%doc LICENSE AUTHORS README.rst HACKING.rst README.SUSE
-%else
+%{!?_licensedir:%global license %doc}
 %license LICENSE
 %doc AUTHORS README.rst HACKING.rst README.SUSE
-%endif
 #
 %dir        %attr(0750, root, salt) %{_sysconfdir}/salt
 %dir        %attr(0750, root, salt) %{_sysconfdir}/salt/pki
@@ -1632,9 +1733,17 @@
 
 %files standalone-formulas-configuration
 %defattr(-,root,root)
+%dir               %attr(0755, root, salt) %{_sysconfdir}/salt/master.d/
 %config(noreplace) %attr(0640, root, salt) 
%{_sysconfdir}/salt/master.d/standalone-formulas-configuration.conf
+%dir               %attr(0750, root, root) %{_sysconfdir}/salt/minion.d/
+%config(noreplace) %attr(0640, root, root) 
%{_sysconfdir}/salt/minion.d/standalone-formulas-configuration.conf
 %dir               %attr(0755, root, salt) %{_prefix}/share/salt-formulas/
 %dir               %attr(0755, root, salt) 
%{_prefix}/share/salt-formulas/states/
 %dir               %attr(0755, root, salt) 
%{_prefix}/share/salt-formulas/metadata/
 
+%files transactional-update
+%defattr(-,root,root)
+%config(noreplace) %attr(0640, root, root) 
%{_sysconfdir}/salt/minion.d/transactional_update.conf
+
+
 %changelog

++++++ _lastrevision ++++++
--- /var/tmp/diff_new_pack.WWBdJC/_old  2021-09-20 23:33:02.939165854 +0200
+++ /var/tmp/diff_new_pack.WWBdJC/_new  2021-09-20 23:33:02.939165854 +0200
@@ -1 +1 @@
-a94708ad2eba9aa15413d989ab3361b2c980589e
\ No newline at end of file
+71392e10750f7481475066788a23a39ad92d0c64
\ No newline at end of file

++++++ add-missing-aarch64-to-rpm-package-architectures-405.patch ++++++
>From e7723f081cc79088156a986cf940349fec7f00a3 Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com>
Date: Wed, 18 Aug 2021 15:05:42 +0300
Subject: [PATCH] Add missing aarch64 to rpm package architectures
 (#405)

Required to prevent false negative results on using pkg.installed
with architecture specification in package name (ex. `bash.aarch64`)
---
 salt/utils/pkg/rpm.py                |  2 +-
 tests/unit/modules/test_zypperpkg.py | 20 ++++++++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/salt/utils/pkg/rpm.py b/salt/utils/pkg/rpm.py
index d1b149ea0b..8b8ea2e4b1 100644
--- a/salt/utils/pkg/rpm.py
+++ b/salt/utils/pkg/rpm.py
@@ -33,7 +33,7 @@ ARCHES_ALPHA = (
     "alphaev68",
     "alphaev7",
 )
-ARCHES_ARM = ("armv5tel", "armv5tejl", "armv6l", "armv7l")
+ARCHES_ARM = ("armv5tel", "armv5tejl", "armv6l", "armv7l", "aarch64")
 ARCHES_SH = ("sh3", "sh4", "sh4a")
 
 ARCHES = (
diff --git a/tests/unit/modules/test_zypperpkg.py 
b/tests/unit/modules/test_zypperpkg.py
index 5c01bbbfbd..d6a6a6d852 100644
--- a/tests/unit/modules/test_zypperpkg.py
+++ b/tests/unit/modules/test_zypperpkg.py
@@ -2477,3 +2477,23 @@ pattern() = package-c"""
         with patch("salt.modules.zypperpkg.__zypper__", zypper_mock):
             assert zypper.services_need_restart() == expected
             zypper_mock(root=None).nolock.call.assert_called_with("ps", "-sss")
+
+    def test_normalize_name(self):
+        """
+        Test that package is normalized only when it should be
+        """
+        with patch.dict(zypper.__grains__, {"osarch": "x86_64"}):
+            result = zypper.normalize_name("foo")
+            assert result == "foo", result
+            result = zypper.normalize_name("foo.x86_64")
+            assert result == "foo", result
+            result = zypper.normalize_name("foo.noarch")
+            assert result == "foo", result
+
+        with patch.dict(zypper.__grains__, {"osarch": "aarch64"}):
+            result = zypper.normalize_name("foo")
+            assert result == "foo", result
+            result = zypper.normalize_name("foo.aarch64")
+            assert result == "foo", result
+            result = zypper.normalize_name("foo.noarch")
+            assert result == "foo", result
-- 
2.32.0


++++++ adding-preliminary-support-for-rocky.-59682-391.patch ++++++
>From 34a913b0b54b55edf042dc899250e56ef0eaec77 Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com>
Date: Mon, 5 Jul 2021 18:57:26 +0300
Subject: [PATCH] Adding preliminary support for Rocky. (#59682) (#391)

* Adding preliminary support for Rocky.

* Adding changelog and test per MR guidence.

* Update test_core.py

Fix a clean up issue

Co-authored-by: Megan Wilhite <megan.wilh...@gmail.com>
Co-authored-by: Gareth J. Greenaway <gar...@wiked.org>

Co-authored-by: StackKorora <42156355+stackkor...@users.noreply.github.com>
Co-authored-by: Megan Wilhite <megan.wilh...@gmail.com>
Co-authored-by: Gareth J. Greenaway <gar...@wiked.org>
---
 changelog/59682.added          |  1 +
 salt/grains/core.py            |  2 ++
 tests/unit/grains/test_core.py | 29 +++++++++++++++++++++++++++++
 3 files changed, 32 insertions(+)
 create mode 100644 changelog/59682.added

diff --git a/changelog/59682.added b/changelog/59682.added
new file mode 100644
index 0000000000..93b4a3d1fc
--- /dev/null
+++ b/changelog/59682.added
@@ -0,0 +1 @@
+Rocky Linux has been added to the RedHat os_family.
diff --git a/salt/grains/core.py b/salt/grains/core.py
index 2b965a2a8a..ace0e4bff9 100644
--- a/salt/grains/core.py
+++ b/salt/grains/core.py
@@ -1547,6 +1547,7 @@ _OS_NAME_MAP = {
     "slesexpand": "RES",
     "linuxmint": "Mint",
     "neon": "KDE neon",
+    "rocky": "Rocky",
     "alibabaclo": "Alinux",
 }
 
@@ -1621,6 +1622,7 @@ _OS_FAMILY_MAP = {
     "Funtoo": "Gentoo",
     "AIX": "AIX",
     "TurnKey": "Debian",
+    "Rocky": "RedHat",
     "AstraLinuxCE": "Debian",
     "Alinux": "RedHat",
 }
diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py
index 8280d6de47..61a6956e32 100644
--- a/tests/unit/grains/test_core.py
+++ b/tests/unit/grains/test_core.py
@@ -678,6 +678,35 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin):
         }
         self._run_os_grains_tests(None, _os_release_map, expectation)
 
+    @skipIf(not salt.utils.platform.is_linux(), "System is not Linux")
+    def test_rocky_8_os_grains(self):
+        """
+        Test if OS grains are parsed correctly in Rocky 8
+        """
+        _os_release_map = {
+            "os_release_file": {
+                "NAME": "Rocky",
+                "VERSION_ID": "8.3",
+                "PRETTY_NAME": "Rocky 8",
+                "ID": "Rocky",
+                "ANSI_COLOR": "0;31",
+                "CPE_NAME": "cpe:/o:rocky:rocky:8.3",
+            },
+            "_linux_distribution": ("rocky", "8.3", ""),
+        }
+
+        expectation = {
+            "os": "Rocky",
+            "os_family": "RedHat",
+            "oscodename": "Rocky 8",
+            "osfullname": "Rocky",
+            "osrelease": "8.3",
+            "osrelease_info": (8, 3,),
+            "osmajorrelease": 8,
+            "osfinger": "Rocky-8",
+        }
+        self._run_os_grains_tests(None, _os_release_map, expectation)
+
     @skipIf(not salt.utils.platform.is_linux(), "System is not Linux")
     def test_almalinux_8_os_grains(self):
         """
-- 
2.32.0


++++++ backport-of-upstream-pr59492-to-3002.2-404.patch ++++++
>From fba6631e0a66a5f8ea76a104e9acf385ce06471c Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com>
Date: Wed, 18 Aug 2021 15:05:30 +0300
Subject: [PATCH] Backport of upstream PR59492 to 3002.2 (#404)

* Fix failing integration tests

* Fix unless logic and failing tests

* Revert some of the changes in the onlyif code

Co-authored-by: twangboy <s...@saltstack.com>
---
 salt/state.py                                 | 24 +++++++++------
 .../files/file/base/issue-35384.sls           |  7 +++++
 tests/unit/test_state.py                      | 30 ++++++++++++++-----
 3 files changed, 44 insertions(+), 17 deletions(-)

diff --git a/salt/state.py b/salt/state.py
index 070a914636..64c5225728 100644
--- a/salt/state.py
+++ b/salt/state.py
@@ -929,7 +929,8 @@ class State:
 
     def _run_check_onlyif(self, low_data, cmd_opts):
         """
-        Check that unless doesn't return 0, and that onlyif returns a 0.
+        Make sure that all commands return True for the state to run. If any
+        command returns False (non 0), the state will not run
         """
         ret = {"result": False}
 
@@ -938,7 +939,9 @@ class State:
         else:
             low_data_onlyif = low_data["onlyif"]
 
+        # If any are False the state will NOT run
         def _check_cmd(cmd):
+            # Don't run condition (False)
             if cmd != 0 and ret["result"] is False:
                 ret.update(
                     {
@@ -1001,7 +1004,8 @@ class State:
 
     def _run_check_unless(self, low_data, cmd_opts):
         """
-        Check that unless doesn't return 0, and that onlyif returns a 0.
+        Check if any of the commands return False (non 0). If any are False the
+        state will run.
         """
         ret = {"result": False}
 
@@ -1010,8 +1014,10 @@ class State:
         else:
             low_data_unless = low_data["unless"]
 
+        # If any are False the state will run
         def _check_cmd(cmd):
-            if cmd == 0 and ret["result"] is False:
+            # Don't run condition
+            if cmd == 0:
                 ret.update(
                     {
                         "comment": "unless condition is true",
@@ -1020,9 +1026,10 @@ class State:
                     }
                 )
                 return False
-            elif cmd != 0:
+            else:
+                ret.pop("skip_watch", None)
                 ret.update({"comment": "unless condition is false", "result": 
False})
-            return True
+                return True
 
         for entry in low_data_unless:
             if isinstance(entry, str):
@@ -1034,7 +1041,7 @@ class State:
                 except CommandExecutionError:
                     # Command failed, so notify unless to skip the item
                     cmd = 0
-                if not _check_cmd(cmd):
+                if _check_cmd(cmd):
                     return ret
             elif isinstance(entry, dict):
                 if "fun" not in entry:
@@ -1047,7 +1054,7 @@ class State:
                 if get_return:
                     result = salt.utils.data.traverse_dict_and_list(result, 
get_return)
                 if self.state_con.get("retcode", 0):
-                    if not _check_cmd(self.state_con["retcode"]):
+                    if _check_cmd(self.state_con["retcode"]):
                         return ret
                 elif result:
                     ret.update(
@@ -1057,11 +1064,11 @@ class State:
                             "result": True,
                         }
                     )
-                    return ret
                 else:
                     ret.update(
                         {"comment": "unless condition is false", "result": 
False}
                     )
+                    return ret
             else:
                 ret.update(
                     {
@@ -1069,7 +1076,6 @@ class State:
                         "result": False,
                     }
                 )
-                return ret
 
         # No reason to stop, return ret
         return ret
diff --git a/tests/integration/files/file/base/issue-35384.sls 
b/tests/integration/files/file/base/issue-35384.sls
index 3c41617ca8..2aa436bb37 100644
--- a/tests/integration/files/file/base/issue-35384.sls
+++ b/tests/integration/files/file/base/issue-35384.sls
@@ -2,5 +2,12 @@ cmd_run_unless_multiple:
   cmd.run:
     - name: echo "hello"
     - unless:
+  {% if grains["os"] ==  "Windows" %}
+      - "exit 0"
+      - "exit 1"
+      - "exit 0"
+  {% else %}
       - "$(which true)"
       - "$(which false)"
+      - "$(which true)"
+  {% endif %}
diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py
index 95018a9cf3..79a261d837 100644
--- a/tests/unit/test_state.py
+++ b/tests/unit/test_state.py
@@ -142,7 +142,7 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
     def test_verify_onlyif_cmd_error(self):
         """
         Simulates a failure in cmd.retcode from onlyif
-        This could occur is runas is specified with a user that does not exist
+        This could occur if runas is specified with a user that does not exist
         """
         low_data = {
             "onlyif": "somecommand",
@@ -175,7 +175,7 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
     def test_verify_unless_cmd_error(self):
         """
         Simulates a failure in cmd.retcode from unless
-        This could occur is runas is specified with a user that does not exist
+        This could occur if runas is specified with a user that does not exist
         """
         low_data = {
             "unless": "somecommand",
@@ -206,6 +206,10 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
                 self.assertEqual(expected_result, return_result)
 
     def test_verify_unless_list_cmd(self):
+        """
+        If any of the unless commands return False (non 0) then the state 
should
+        run (no skip_watch).
+        """
         low_data = {
             "state": "cmd",
             "name": 'echo "something"',
@@ -217,9 +221,8 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
             "fun": "run",
         }
         expected_result = {
-            "comment": "unless condition is true",
-            "result": True,
-            "skip_watch": True,
+            "comment": "unless condition is false",
+            "result": False,
         }
         with patch("salt.state.State._gather_pillar") as state_patch:
             minion_opts = self.get_temp_config("minion")
@@ -228,6 +231,10 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
             self.assertEqual(expected_result, return_result)
 
     def test_verify_unless_list_cmd_different_order(self):
+        """
+        If any of the unless commands return False (non 0) then the state 
should
+        run (no skip_watch). The order shouldn't matter.
+        """
         low_data = {
             "state": "cmd",
             "name": 'echo "something"',
@@ -239,9 +246,8 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
             "fun": "run",
         }
         expected_result = {
-            "comment": "unless condition is true",
-            "result": True,
-            "skip_watch": True,
+            "comment": "unless condition is false",
+            "result": False,
         }
         with patch("salt.state.State._gather_pillar") as state_patch:
             minion_opts = self.get_temp_config("minion")
@@ -272,6 +278,10 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
             self.assertEqual(expected_result, return_result)
 
     def test_verify_unless_list_cmd_valid(self):
+        """
+        If any of the unless commands return False (non 0) then the state 
should
+        run (no skip_watch). This tests all commands return False.
+        """
         low_data = {
             "state": "cmd",
             "name": 'echo "something"',
@@ -308,6 +318,10 @@ class StateCompilerTestCase(TestCase, 
AdaptedConfigurationTestCaseMixin):
             self.assertEqual(expected_result, return_result)
 
     def test_verify_unless_list_cmd_invalid(self):
+        """
+        If any of the unless commands return False (non 0) then the state 
should
+        run (no skip_watch). This tests all commands return True
+        """
         low_data = {
             "state": "cmd",
             "name": 'echo "something"',
-- 
2.32.0


++++++ backport-thread.is_alive-fix-390.patch ++++++
>From a782af246a2f3d4b91afee2ee847c87f71e8904b Mon Sep 17 00:00:00 2001
From: Alexander Graul <agr...@suse.com>
Date: Fri, 25 Jun 2021 13:34:38 +0200
Subject: [PATCH] Backport Thread.is_alive fix (#390)

* Change thread.isAlive() to thread.is_alive()

(cherry picked from commit b1dc0cee03896c8abad55a609805b0be6c7aaefa)

* Run pre-commit on salt/utils/timed_subprocess.py

(cherry picked from commit 178e3b83e6c21abf5d6db454c19c104ceb8bd92c)

* Fix the six removal made by pre-commit

(cherry picked from commit aaa8ca3b7f129568637799d6d49d7ad3708f73bc)

* Remove the PY2 code in salt/utils/timed_subprocess.py

(cherry picked from commit 3a702a510b965e9af1ad318c953e19114925357e)

Co-authored-by: Petr Messner <p...@leadhub.co>
Co-authored-by: Petr Messner <petr.mess...@gmail.com>
---
 salt/utils/timed_subprocess.py | 39 ++++++++++++++--------------------
 1 file changed, 16 insertions(+), 23 deletions(-)

diff --git a/salt/utils/timed_subprocess.py b/salt/utils/timed_subprocess.py
index 5c4ac35ac3..b043a3bde2 100644
--- a/salt/utils/timed_subprocess.py
+++ b/salt/utils/timed_subprocess.py
@@ -1,8 +1,6 @@
-# -*- coding: utf-8 -*-
 """
 For running command line executables with a timeout
 """
-from __future__ import absolute_import, print_function, unicode_literals
 
 import shlex
 import subprocess
@@ -10,10 +8,9 @@ import threading
 
 import salt.exceptions
 import salt.utils.data
-from salt.ext import six
 
 
-class TimedProc(object):
+class TimedProc:
     """
     Create a TimedProc object, calls subprocess.Popen with passed args and 
**kwargs
     """
@@ -46,7 +43,7 @@ class TimedProc(object):
 
         if self.timeout and not isinstance(self.timeout, (int, float)):
             raise salt.exceptions.TimedProcTimeoutError(
-                "Error: timeout {0} must be a number".format(self.timeout)
+                "Error: timeout {} must be a number".format(self.timeout)
             )
         if kwargs.get("shell", False):
             args = salt.utils.data.decode(args, to_str=True)
@@ -59,28 +56,24 @@ class TimedProc(object):
                     try:
                         args = shlex.split(args)
                     except AttributeError:
-                        args = shlex.split(six.text_type(args))
+                        args = shlex.split(str(args))
                 str_args = []
                 for arg in args:
-                    if not isinstance(arg, six.string_types):
-                        str_args.append(six.text_type(arg))
+                    if not isinstance(arg, str):
+                        str_args.append(str(arg))
                     else:
                         str_args.append(arg)
                 args = str_args
             else:
-                if not isinstance(args, (list, tuple, six.string_types)):
+                if not isinstance(args, (list, tuple, str)):
                     # Handle corner case where someone does a 'cmd.run 3'
-                    args = six.text_type(args)
+                    args = str(args)
             # Ensure that environment variables are strings
-            for key, val in six.iteritems(kwargs.get("env", {})):
-                if not isinstance(val, six.string_types):
-                    kwargs["env"][key] = six.text_type(val)
-                if not isinstance(key, six.string_types):
-                    kwargs["env"][six.text_type(key)] = kwargs["env"].pop(key)
-            if six.PY2 and "env" in kwargs:
-                # Ensure no unicode in custom env dict, as it can cause
-                # problems with subprocess.
-                kwargs["env"] = salt.utils.data.encode_dict(kwargs["env"])
+            for key, val in kwargs.get("env", {}).items():
+                if not isinstance(val, str):
+                    kwargs["env"][key] = str(val)
+                if not isinstance(key, str):
+                    kwargs["env"][str(key)] = kwargs["env"].pop(key)
             args = salt.utils.data.decode(args)
             self.process = subprocess.Popen(args, **kwargs)
         self.command = args
@@ -103,18 +96,18 @@ class TimedProc(object):
             rt = threading.Thread(target=receive)
             rt.start()
             rt.join(self.timeout)
-            if rt.isAlive():
+            if rt.is_alive():
                 # Subprocess cleanup (best effort)
                 self.process.kill()
 
                 def terminate():
-                    if rt.isAlive():
+                    if rt.is_alive():
                         self.process.terminate()
 
                 threading.Timer(10, terminate).start()
                 raise salt.exceptions.TimedProcTimeoutError(
-                    "{0} : Timed out after {1} seconds".format(
-                        self.command, six.text_type(self.timeout),
+                    "{} : Timed out after {} seconds".format(
+                        self.command, str(self.timeout),
                     )
                 )
         return self.process.returncode
-- 
2.32.0


++++++ better-handling-of-bad-public-keys-from-minions-bsc-.patch ++++++
>From cd64b9a063771829f85d6be0e42259825cfb10c8 Mon Sep 17 00:00:00 2001
From: "Daniel A. Wozniak" <dwozn...@saltstack.com>
Date: Mon, 2 Aug 2021 13:50:37 -0700
Subject: [PATCH] Better handling of bad public keys from minions
 (bsc#1189040)

Add changelog for #57733

Fix pre-commit check

Add missing test

Fix test on older pythons
---
 changelog/57733.fixed                         |  1 +
 salt/crypt.py                                 | 11 ++++++--
 salt/exceptions.py                            |  6 ++++
 salt/key.py                                   | 15 ++++++++--
 salt/transport/mixins/auth.py                 | 12 ++++----
 .../pytests/integration/cli/test_salt_key.py  | 28 +++++++++++++++++++
 tests/pytests/unit/test_crypt.py              | 20 +++++++++++++
 7 files changed, 83 insertions(+), 10 deletions(-)
 create mode 100644 changelog/57733.fixed
 create mode 100644 tests/pytests/unit/test_crypt.py

diff --git a/changelog/57733.fixed b/changelog/57733.fixed
new file mode 100644
index 0000000000..0cd55b19a6
--- /dev/null
+++ b/changelog/57733.fixed
@@ -0,0 +1 @@
+Better handling of bad RSA public keys from minions
diff --git a/salt/crypt.py b/salt/crypt.py
index 0a8b728f50..e6e4f3181e 100644
--- a/salt/crypt.py
+++ b/salt/crypt.py
@@ -36,6 +36,7 @@ import salt.utils.verify
 import salt.version
 from salt.exceptions import (
     AuthenticationError,
+    InvalidKeyError,
     MasterExit,
     SaltClientError,
     SaltReqTimeoutError,
@@ -217,10 +218,16 @@ def get_rsa_pub_key(path):
         with salt.utils.files.fopen(path, "rb") as f:
             data = f.read().replace(b"RSA ", b"")
         bio = BIO.MemoryBuffer(data)
-        key = RSA.load_pub_key_bio(bio)
+        try:
+            key = RSA.load_pub_key_bio(bio)
+        except RSA.RSAError:
+            raise InvalidKeyError("Encountered bad RSA public key")
     else:
         with salt.utils.files.fopen(path) as f:
-            key = RSA.importKey(f.read())
+            try:
+                key = RSA.importKey(f.read())
+            except (ValueError, IndexError, TypeError):
+                raise InvalidKeyError("Encountered bad RSA public key")
     return key
 
 
diff --git a/salt/exceptions.py b/salt/exceptions.py
index 033a19cc54..1da15f9e69 100644
--- a/salt/exceptions.py
+++ b/salt/exceptions.py
@@ -111,6 +111,12 @@ class AuthenticationError(SaltException):
     """
 
 
+class InvalidKeyError(SaltException):
+    """
+    Raised when we encounter an invalid RSA key.
+    """
+
+
 class CommandNotFoundError(SaltException):
     """
     Used in modules or grains when a required binary is not available
diff --git a/salt/key.py b/salt/key.py
index 75777ede06..59090c979c 100644
--- a/salt/key.py
+++ b/salt/key.py
@@ -11,6 +11,7 @@ import fnmatch
 import logging
 import os
 import shutil
+import sys
 
 # Import salt libs
 import salt.cache
@@ -652,17 +653,27 @@ class Key(object):
             keydirs.append(self.REJ)
         if include_denied:
             keydirs.append(self.DEN)
+        invalid_keys = []
         for keydir in keydirs:
             for key in matches.get(keydir, []):
+                key_path = os.path.join(self.opts["pki_dir"], keydir, key)
+                try:
+                    salt.crypt.get_rsa_pub_key(key_path)
+                except salt.exceptions.InvalidKeyError:
+                    log.error("Invalid RSA public key: %s", key)
+                    invalid_keys.append((keydir, key))
+                    continue
                 try:
                     shutil.move(
-                        os.path.join(self.opts["pki_dir"], keydir, key),
-                        os.path.join(self.opts["pki_dir"], self.ACC, key),
+                        key_path, os.path.join(self.opts["pki_dir"], self.ACC, 
key),
                     )
                     eload = {"result": True, "act": "accept", "id": key}
                     self.event.fire_event(eload, 
salt.utils.event.tagify(prefix="key"))
                 except (IOError, OSError):
                     pass
+        for keydir, key in invalid_keys:
+            matches[keydir].remove(key)
+            sys.stderr.write("Unable to accept invalid key for 
{}.\n".format(key))
         return self.name_match(match) if match is not None else 
self.dict_match(matches)
 
     def accept_all(self):
diff --git a/salt/transport/mixins/auth.py b/salt/transport/mixins/auth.py
index 003cbd8275..0f0c615408 100644
--- a/salt/transport/mixins/auth.py
+++ b/salt/transport/mixins/auth.py
@@ -184,11 +184,11 @@ class AESReqServerMixin(object):
         tagged "auth" and returns a dict with information about the auth
         event
 
-        # Verify that the key we are receiving matches the stored key
-        # Store the key if it is not there
-        # Make an RSA key with the pub key
-        # Encrypt the AES key as an encrypted salt.payload
-        # Package the return and return it
+            - Verify that the key we are receiving matches the stored key
+            - Store the key if it is not there
+            - Make an RSA key with the pub key
+            - Encrypt the AES key as an encrypted salt.payload
+            - Package the return and return it
         """
 
         if not salt.utils.verify.valid_id(self.opts, load["id"]):
@@ -460,7 +460,7 @@ class AESReqServerMixin(object):
         # and an empty request comes in
         try:
             pub = salt.crypt.get_rsa_pub_key(pubfn)
-        except (ValueError, IndexError, TypeError) as err:
+        except salt.crypt.InvalidKeyError as err:
             log.error('Corrupt public key "%s": %s', pubfn, err)
             return {"enc": "clear", "load": {"ret": False}}
 
diff --git a/tests/pytests/integration/cli/test_salt_key.py 
b/tests/pytests/integration/cli/test_salt_key.py
index 0edb2cf86c..2583348ce6 100644
--- a/tests/pytests/integration/cli/test_salt_key.py
+++ b/tests/pytests/integration/cli/test_salt_key.py
@@ -328,3 +328,31 @@ def test_keys_generation_keysize_max(salt_key_cli):
         )
         assert ret.exitcode != 0
         assert "error: The maximum value for keysize is 32768" in ret.stderr
+
+def test_keys_generation_keysize_max(salt_key_cli, tmp_path):
+    ret = salt_key_cli.run(
+        "--gen-keys", "minibar", "--gen-keys-dir", str(tmp_path), "--keysize", 
"32769"
+    )
+    assert ret.exitcode != 0
+    assert "error: The maximum value for keysize is 32768" in ret.stderr
+
+
+def test_accept_bad_key(salt_master, salt_key_cli):
+    """
+    test salt-key -d usage
+    """
+    min_name = random_string("minibar-")
+    pki_dir = salt_master.config["pki_dir"]
+    key = os.path.join(pki_dir, "minions_pre", min_name)
+
+    with salt.utils.files.fopen(key, "w") as fp:
+        fp.write("")
+
+    try:
+        # Check Key
+        ret = salt_key_cli.run("-y", "-a", min_name)
+        assert ret.exitcode == 0
+        assert "invalid key for {}".format(min_name) in ret.stderr
+    finally:
+        if os.path.exists(key):
+            os.remove(key)
diff --git a/tests/pytests/unit/test_crypt.py b/tests/pytests/unit/test_crypt.py
new file mode 100644
index 0000000000..aa8f439b8c
--- /dev/null
+++ b/tests/pytests/unit/test_crypt.py
@@ -0,0 +1,20 @@
+"""
+tests.pytests.unit.test_crypt
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unit tests for salt's crypt module
+"""
+import pytest
+import salt.crypt
+import salt.utils.files
+
+
+def test_get_rsa_pub_key_bad_key(tmp_path):
+    """
+    get_rsa_pub_key raises InvalidKeyError when encoutering a bad key
+    """
+    key_path = str(tmp_path / "key")
+    with salt.utils.files.fopen(key_path, "w") as fp:
+        fp.write("")
+    with pytest.raises(salt.crypt.InvalidKeyError):
+        salt.crypt.get_rsa_pub_key(key_path)
-- 
2.32.0


++++++ check-if-dpkgnotify-is-executable-bsc-1186674-376.patch ++++++
>From b477b00447b49fc2f221cfb6d2c491bcd1970119 Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com>
Date: Tue, 1 Jun 2021 13:04:43 +0300
Subject: [PATCH] Check if dpkgnotify is executable (bsc#1186674)
 (#376)

It prevents fails on removing salt-minion package
when the dpkg configuration is still active
---
 scripts/suse/dpkg/99dpkgnotify | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/suse/dpkg/99dpkgnotify b/scripts/suse/dpkg/99dpkgnotify
index 8013387a57..f89815f605 100644
--- a/scripts/suse/dpkg/99dpkgnotify
+++ b/scripts/suse/dpkg/99dpkgnotify
@@ -1 +1 @@
-DPkg::Post-Invoke {"/usr/bin/dpkgnotify";};
+DPkg::Post-Invoke {"if [ -x /usr/bin/dpkgnotify ]; then /usr/bin/dpkgnotify; 
fi;";};
-- 
2.31.1


++++++ do-noop-for-services-states-when-running-systemd-in-.patch ++++++
++++ 631 lines (skipped)

++++++ don-t-use-shell-sbin-nologin-in-requisites.patch ++++++
>From 9a8ca020a3cacbcfbbc33f209cd0ea6c3da3f788 Mon Sep 17 00:00:00 2001
From: Alexander Graul <agr...@suse.com>
Date: Tue, 17 Aug 2021 11:52:00 +0200
Subject: [PATCH] Don't use shell="/sbin/nologin" in requisites

Using shell="/sbin/nologin" in an onlyif/unless requisite does not
really make sense since the condition can't be run. shell=/sbin/nologin
is also a common argument, e.g. for user.present.

Fixes: bsc#1188259
---
 salt/state.py | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/salt/state.py b/salt/state.py
index 64c5225728..c6742101b2 100644
--- a/salt/state.py
+++ b/salt/state.py
@@ -889,9 +889,14 @@ class State:
             cmd_opts[run_cmd_arg] = low_data.get(run_cmd_arg)
 
         if "shell" in low_data:
-            cmd_opts["shell"] = low_data["shell"]
+            shell = low_data["shell"]
         elif "shell" in self.opts["grains"]:
-            cmd_opts["shell"] = self.opts["grains"].get("shell")
+            shell = self.opts["grains"].get("shell")
+        else:
+            shell = None
+        # /sbin/nologin always causes the onlyif / unless cmd to fail
+        if shell is not None and shell != "/sbin/nologin":
+            cmd_opts["shell"] = shell
 
         if "onlyif" in low_data:
             _ret = self._run_check_onlyif(low_data, cmd_opts)
-- 
2.32.0


++++++ enhance-logging-when-inotify-beacon-is-missing-pyino.patch ++++++
>From cde0f9385e1afb9fa97fe2c86cfa77ae3b899aa0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernan...@suse.com>
Date: Fri, 18 Jun 2021 13:09:22 +0100
Subject: [PATCH] Enhance logging when inotify beacon is missing
 pyinotify (bsc#1186310)

---
 salt/beacons/inotify.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/salt/beacons/inotify.py b/salt/beacons/inotify.py
index fa2f73c35f..a6b7548f97 100644
--- a/salt/beacons/inotify.py
+++ b/salt/beacons/inotify.py
@@ -49,7 +49,9 @@ log = logging.getLogger(__name__)
 def __virtual__():
     if HAS_PYINOTIFY:
         return __virtualname__
-    return False
+    err_msg = "pyinotify library is missing"
+    log.error("Unable to load inotify beacon: {}".format(err_msg))
+    return False, err_msg
 
 
 def _get_mask(mask):
-- 
2.31.1


++++++ enhance-openscap-module-add-xccdf_eval-call-386.patch ++++++
>From 1fd51c17bc03e679a040f2c6d9ac107a2c57b7c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernan...@suse.com>
Date: Wed, 7 Jul 2021 15:41:48 +0100
Subject: [PATCH] Enhance openscap module: add "xccdf_eval" call (#386)

* Enhance openscap module: add xccdf_eval call

* Allow 'tailoring_file' and 'tailoring_id' parameters

* Fix wrong reference to subprocess.PIPE in openscap unit tests

* Add changes suggested by pre-commit

Co-authored-by: Michael Calmer <m...@suse.de>
---
 changelog/59756.added               |   1 +
 salt/modules/openscap.py            | 120 ++++++++++++-
 tests/unit/modules/test_openscap.py | 262 +++++++++++++++++++++++++---
 3 files changed, 353 insertions(+), 30 deletions(-)
 create mode 100644 changelog/59756.added

diff --git a/changelog/59756.added b/changelog/59756.added
new file mode 100644
index 0000000000..a59fb21eef
--- /dev/null
+++ b/changelog/59756.added
@@ -0,0 +1 @@
+adding new call for openscap xccdf eval supporting new parameters
diff --git a/salt/modules/openscap.py b/salt/modules/openscap.py
index 6f8ff4a76d..f75e1c5e6b 100644
--- a/salt/modules/openscap.py
+++ b/salt/modules/openscap.py
@@ -1,20 +1,15 @@
-# -*- coding: utf-8 -*-
 """
 Module for OpenSCAP Management
 
 """
 
-# Import Python libs
-from __future__ import absolute_import, print_function, unicode_literals
 
+import os.path
 import shlex
 import shutil
 import tempfile
 from subprocess import PIPE, Popen
 
-# Import Salt libs
-from salt.ext import six
-
 ArgumentParser = object
 
 try:
@@ -44,7 +39,7 @@ def __virtual__():
 
 class _ArgumentParser(ArgumentParser):
     def __init__(self, action=None, *args, **kwargs):
-        super(_ArgumentParser, self).__init__(*args, prog="oscap", **kwargs)
+        super().__init__(*args, prog="oscap", **kwargs)
         self.add_argument("action", choices=["eval"])
         add_arg = None
         for params, kwparams in _XCCDF_MAP["eval"]["parser_arguments"]:
@@ -61,6 +56,115 @@ _OSCAP_EXIT_CODES_MAP = {
 }
 
 
+def xccdf_eval(xccdffile, ovalfiles=None, **kwargs):
+    """
+    Run ``oscap xccdf eval`` commands on minions.
+    It uses cp.push_dir to upload the generated files to the salt master
+    in the master's minion files cachedir
+    (defaults to ``/var/cache/salt/master/minions/minion-id/files``)
+
+    It needs ``file_recv`` set to ``True`` in the master configuration file.
+
+    xccdffile
+        the path to the xccdf file to evaluate
+
+    ovalfiles
+        additional oval definition files
+
+    profile
+        the name of Profile to be evaluated
+
+    rule
+        the name of a single rule to be evaluated
+
+    oval_results
+        save OVAL results as well (True or False)
+
+    results
+        write XCCDF Results into given file
+
+    report
+        write HTML report into given file
+
+    fetch_remote_resources
+        download remote content referenced by XCCDF (True or False)
+
+    tailoring_file
+        use given XCCDF Tailoring file
+
+    tailoring_id
+        use given DS component as XCCDF Tailoring file
+
+    remediate
+        automatically execute XCCDF fix elements for failed rules.
+        Use of this option is always at your own risk. (True or False)
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*'  openscap.xccdf_eval 
/usr/share/openscap/scap-yast2sec-xccdf.xml profile=Default
+
+    """
+    success = True
+    error = None
+    upload_dir = None
+    returncode = None
+    if not ovalfiles:
+        ovalfiles = []
+
+    cmd_opts = ["oscap", "xccdf", "eval"]
+    if kwargs.get("oval_results"):
+        cmd_opts.append("--oval-results")
+    if "results" in kwargs:
+        cmd_opts.append("--results")
+        cmd_opts.append(kwargs["results"])
+    if "report" in kwargs:
+        cmd_opts.append("--report")
+        cmd_opts.append(kwargs["report"])
+    if "profile" in kwargs:
+        cmd_opts.append("--profile")
+        cmd_opts.append(kwargs["profile"])
+    if "rule" in kwargs:
+        cmd_opts.append("--rule")
+        cmd_opts.append(kwargs["rule"])
+    if "tailoring_file" in kwargs:
+        cmd_opts.append("--tailoring-file")
+        cmd_opts.append(kwargs["tailoring_file"])
+    if "tailoring_id" in kwargs:
+        cmd_opts.append("--tailoring-id")
+        cmd_opts.append(kwargs["tailoring_id"])
+    if kwargs.get("fetch_remote_resources"):
+        cmd_opts.append("--fetch-remote-resources")
+    if kwargs.get("remediate"):
+        cmd_opts.append("--remediate")
+    cmd_opts.append(xccdffile)
+    cmd_opts.extend(ovalfiles)
+
+    if not os.path.exists(xccdffile):
+        success = False
+        error = "XCCDF File '{}' does not exist".format(xccdffile)
+    for ofile in ovalfiles:
+        if success and not os.path.exists(ofile):
+            success = False
+            error = "Oval File '{}' does not exist".format(ofile)
+
+    if success:
+        tempdir = tempfile.mkdtemp()
+        proc = Popen(cmd_opts, stdout=PIPE, stderr=PIPE, cwd=tempdir)
+        (stdoutdata, error) = proc.communicate()
+        success = _OSCAP_EXIT_CODES_MAP[proc.returncode]
+        returncode = proc.returncode
+        if success:
+            __salt__["cp.push_dir"](tempdir)
+            upload_dir = tempdir
+        shutil.rmtree(tempdir, ignore_errors=True)
+
+    return dict(
+        success=success, upload_dir=upload_dir, error=error, 
returncode=returncode
+    )
+
+
 def xccdf(params):
     """
     Run ``oscap xccdf`` commands on minions.
@@ -91,7 +195,7 @@ def xccdf(params):
         args, argv = 
_ArgumentParser(action=action).parse_known_args(args=params)
     except Exception as err:  # pylint: disable=broad-except
         success = False
-        error = six.text_type(err)
+        error = str(err)
 
     if success:
         cmd = _XCCDF_MAP[action]["cmd_pattern"].format(args.profile, policy)
diff --git a/tests/unit/modules/test_openscap.py 
b/tests/unit/modules/test_openscap.py
index 04cf00a1d3..e5be151bf2 100644
--- a/tests/unit/modules/test_openscap.py
+++ b/tests/unit/modules/test_openscap.py
@@ -1,18 +1,8 @@
-# -*- coding: utf-8 -*-
-
-# Import python libs
-from __future__ import absolute_import, print_function, unicode_literals
-
 from subprocess import PIPE
 
-# Import salt libs
 import salt.modules.openscap as openscap
-
-# Import 3rd-party libs
 from salt.ext import six
 from tests.support.mock import MagicMock, Mock, patch
-
-# Import salt test libs
 from tests.support.unit import TestCase
 
 
@@ -32,6 +22,7 @@ class OpenscapTestCase(TestCase):
                 "salt.modules.openscap.tempfile.mkdtemp",
                 Mock(return_value=self.random_temp_dir),
             ),
+            patch("salt.modules.openscap.os.path.exists", 
Mock(return_value=True)),
         ]
         for patcher in patchers:
             self.apply_patch(patcher)
@@ -50,7 +41,7 @@ class OpenscapTestCase(TestCase):
             ),
         ):
             response = openscap.xccdf(
-                "eval --profile Default {0}".format(self.policy_file)
+                "eval --profile Default {}".format(self.policy_file)
             )
 
             self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
@@ -97,7 +88,7 @@ class OpenscapTestCase(TestCase):
             ),
         ):
             response = openscap.xccdf(
-                "eval --profile Default {0}".format(self.policy_file)
+                "eval --profile Default {}".format(self.policy_file)
             )
 
             self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
@@ -136,10 +127,7 @@ class OpenscapTestCase(TestCase):
 
     def test_openscap_xccdf_eval_fail_no_profile(self):
         response = openscap.xccdf("eval --param Default /unknown/param")
-        if six.PY2:
-            error = "argument --profile is required"
-        else:
-            error = "the following arguments are required: --profile"
+        error = "the following arguments are required: --profile"
         self.assertEqual(
             response,
             {"error": error, "upload_dir": None, "success": False, 
"returncode": None},
@@ -199,7 +187,7 @@ class OpenscapTestCase(TestCase):
             ),
         ):
             response = openscap.xccdf(
-                "eval --profile Default {0}".format(self.policy_file)
+                "eval --profile Default {}".format(self.policy_file)
             )
 
             self.assertEqual(
@@ -213,11 +201,8 @@ class OpenscapTestCase(TestCase):
             )
 
     def test_openscap_xccdf_eval_fail_not_implemented_action(self):
-        response = openscap.xccdf("info {0}".format(self.policy_file))
-        if six.PY2:
-            mock_err = "argument action: invalid choice: 'info' (choose from 
u'eval')"
-        else:
-            mock_err = "argument action: invalid choice: 'info' (choose from 
'eval')"
+        response = openscap.xccdf("info {}".format(self.policy_file))
+        mock_err = "argument action: invalid choice: 'info' (choose from 
'eval')"
 
         self.assertEqual(
             response,
@@ -228,3 +213,236 @@ class OpenscapTestCase(TestCase):
                 "returncode": None,
             },
         )
+
+    def test_new_openscap_xccdf_eval_success(self):
+        with patch(
+            "salt.modules.openscap.Popen",
+            MagicMock(
+                return_value=Mock(
+                    **{"returncode": 0, "communicate.return_value": ("", "")}
+                )
+            ),
+        ):
+            response = openscap.xccdf_eval(
+                self.policy_file,
+                profile="Default",
+                oval_results=True,
+                results="results.xml",
+                report="report.html",
+            )
+
+            self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
+            expected_cmd = [
+                "oscap",
+                "xccdf",
+                "eval",
+                "--oval-results",
+                "--results",
+                "results.xml",
+                "--report",
+                "report.html",
+                "--profile",
+                "Default",
+                self.policy_file,
+            ]
+            openscap.Popen.assert_called_once_with(
+                expected_cmd,
+                cwd=openscap.tempfile.mkdtemp.return_value,
+                stderr=PIPE,
+                stdout=PIPE,
+            )
+            openscap.__salt__["cp.push_dir"].assert_called_once_with(
+                self.random_temp_dir
+            )
+            self.assertEqual(openscap.shutil.rmtree.call_count, 1)
+            self.assertEqual(
+                response,
+                {
+                    "upload_dir": self.random_temp_dir,
+                    "error": "",
+                    "success": True,
+                    "returncode": 0,
+                },
+            )
+
+    def test_new_openscap_xccdf_eval_success_with_extra_ovalfiles(self):
+        with patch(
+            "salt.modules.openscap.Popen",
+            MagicMock(
+                return_value=Mock(
+                    **{"returncode": 0, "communicate.return_value": ("", "")}
+                )
+            ),
+        ):
+            response = openscap.xccdf_eval(
+                self.policy_file,
+                ["/usr/share/xml/another-oval.xml", "/usr/share/xml/oval.xml"],
+                profile="Default",
+                oval_results=True,
+                results="results.xml",
+                report="report.html",
+            )
+
+            self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
+            expected_cmd = [
+                "oscap",
+                "xccdf",
+                "eval",
+                "--oval-results",
+                "--results",
+                "results.xml",
+                "--report",
+                "report.html",
+                "--profile",
+                "Default",
+                self.policy_file,
+                "/usr/share/xml/another-oval.xml",
+                "/usr/share/xml/oval.xml",
+            ]
+            openscap.Popen.assert_called_once_with(
+                expected_cmd,
+                cwd=openscap.tempfile.mkdtemp.return_value,
+                stderr=PIPE,
+                stdout=PIPE,
+            )
+            openscap.__salt__["cp.push_dir"].assert_called_once_with(
+                self.random_temp_dir
+            )
+            self.assertEqual(openscap.shutil.rmtree.call_count, 1)
+            self.assertEqual(
+                response,
+                {
+                    "upload_dir": self.random_temp_dir,
+                    "error": "",
+                    "success": True,
+                    "returncode": 0,
+                },
+            )
+
+    def test_new_openscap_xccdf_eval_success_with_failing_rules(self):
+        with patch(
+            "salt.modules.openscap.Popen",
+            MagicMock(
+                return_value=Mock(
+                    **{"returncode": 2, "communicate.return_value": ("", "some 
error")}
+                )
+            ),
+        ):
+            response = openscap.xccdf_eval(
+                self.policy_file,
+                profile="Default",
+                oval_results=True,
+                results="results.xml",
+                report="report.html",
+            )
+
+            self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
+            expected_cmd = [
+                "oscap",
+                "xccdf",
+                "eval",
+                "--oval-results",
+                "--results",
+                "results.xml",
+                "--report",
+                "report.html",
+                "--profile",
+                "Default",
+                self.policy_file,
+            ]
+            openscap.Popen.assert_called_once_with(
+                expected_cmd,
+                cwd=openscap.tempfile.mkdtemp.return_value,
+                stderr=PIPE,
+                stdout=PIPE,
+            )
+            openscap.__salt__["cp.push_dir"].assert_called_once_with(
+                self.random_temp_dir
+            )
+            self.assertEqual(openscap.shutil.rmtree.call_count, 1)
+            self.assertEqual(
+                response,
+                {
+                    "upload_dir": self.random_temp_dir,
+                    "error": "some error",
+                    "success": True,
+                    "returncode": 2,
+                },
+            )
+
+    def test_new_openscap_xccdf_eval_success_ignore_unknown_params(self):
+        with patch(
+            "salt.modules.openscap.Popen",
+            MagicMock(
+                return_value=Mock(
+                    **{"returncode": 2, "communicate.return_value": ("", "some 
error")}
+                )
+            ),
+        ):
+            response = openscap.xccdf_eval(
+                "/policy/file",
+                param="Default",
+                profile="Default",
+                oval_results=True,
+                results="results.xml",
+                report="report.html",
+            )
+
+            self.assertEqual(
+                response,
+                {
+                    "upload_dir": self.random_temp_dir,
+                    "error": "some error",
+                    "success": True,
+                    "returncode": 2,
+                },
+            )
+            expected_cmd = [
+                "oscap",
+                "xccdf",
+                "eval",
+                "--oval-results",
+                "--results",
+                "results.xml",
+                "--report",
+                "report.html",
+                "--profile",
+                "Default",
+                "/policy/file",
+            ]
+            openscap.Popen.assert_called_once_with(
+                expected_cmd,
+                cwd=openscap.tempfile.mkdtemp.return_value,
+                stderr=PIPE,
+                stdout=PIPE,
+            )
+
+    def test_new_openscap_xccdf_eval_evaluation_error(self):
+        with patch(
+            "salt.modules.openscap.Popen",
+            MagicMock(
+                return_value=Mock(
+                    **{
+                        "returncode": 1,
+                        "communicate.return_value": ("", "evaluation error"),
+                    }
+                )
+            ),
+        ):
+            response = openscap.xccdf_eval(
+                self.policy_file,
+                profile="Default",
+                oval_results=True,
+                results="results.xml",
+                report="report.html",
+            )
+
+            self.assertEqual(
+                response,
+                {
+                    "upload_dir": None,
+                    "error": "evaluation error",
+                    "success": False,
+                    "returncode": 1,
+                },
+            )
-- 
2.32.0


++++++ exclude-the-full-path-of-a-download-url-to-prevent-i.patch ++++++
>From 57ed9c41a177f57e3d56465662750617ac36cc95 Mon Sep 17 00:00:00 2001
From: Joe Eacott <jeac...@vmware.com>
Date: Mon, 28 Jun 2021 16:46:35 -0600
Subject: [PATCH] Exclude the full path of a download URL to prevent
 injection of malicious code (bsc#1190265) (CVE-2021-21996)

---
 salt/fileclient.py            |  7 +++++++
 tests/unit/test_fileclient.py | 18 ++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/salt/fileclient.py b/salt/fileclient.py
index 88dcf1668d..bdf450ffe6 100644
--- a/salt/fileclient.py
+++ b/salt/fileclient.py
@@ -28,6 +28,7 @@ import salt.utils.platform
 import salt.utils.stringutils
 import salt.utils.templates
 import salt.utils.url
+import salt.utils.verify
 import salt.utils.versions
 from salt.exceptions import CommandExecutionError, MinionError
 
@@ -858,6 +859,12 @@ class Client:
         else:
             file_name = url_data.path
 
+        # clean_path returns an empty string if the check fails
+        root_path = salt.utils.path.join(cachedir, "extrn_files", saltenv, 
netloc)
+        new_path = os.path.sep.join([root_path, file_name])
+        if not salt.utils.verify.clean_path(root_path, new_path, subdir=True):
+            return "Invalid path"
+
         if len(file_name) > MAX_FILENAME_LENGTH:
             file_name = salt.utils.hashutils.sha256_digest(file_name)
 
diff --git a/tests/unit/test_fileclient.py b/tests/unit/test_fileclient.py
index 3aa7b7cf84..b6cc84a871 100644
--- a/tests/unit/test_fileclient.py
+++ b/tests/unit/test_fileclient.py
@@ -63,6 +63,24 @@ class FileclientTestCase(TestCase):
                     ) as c_ref_itr:
                         assert c_ref_itr == "/__test__/files/base/testfile"
 
+    def test_cache_extrn_path_valid(self):
+        """
+        Tests for extrn_filepath for a given url
+        """
+        file_name = "http://localhost:8000/test/location/src/dev/usr/file";
+
+        ret = fileclient.Client(self.opts)._extrn_path(file_name, "base")
+        assert ret == os.path.join("__test__", "extrn_files", "base", ret)
+
+    def test_cache_extrn_path_invalid(self):
+        """
+        Tests for extrn_filepath for a given url
+        """
+        file_name = "http://localhost:8000/../../../../../usr/bin/bad";
+
+        ret = fileclient.Client(self.opts)._extrn_path(file_name, "base")
+        assert ret == "Invalid path"
+
     def test_extrn_path_with_long_filename(self):
         safe_file_name = os.path.split(
             fileclient.Client(self.opts)._extrn_path(
-- 
2.33.0


++++++ figure-out-python-interpreter-to-use-inside-containe.patch ++++++
>From 271826b0baa6b2281bc2eac9118a0fcc4675f106 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernan...@suse.com>
Date: Wed, 19 May 2021 16:24:27 +0100
Subject: [PATCH] Figure out Python interpreter to use inside
 containers

Fix unit test for dockermod.call function
---
 salt/modules/dockermod.py            | 28 +++++++++++++++++++++++---
 tests/unit/modules/test_dockermod.py | 30 +++++++++++++++-------------
 2 files changed, 41 insertions(+), 17 deletions(-)

diff --git a/salt/modules/dockermod.py b/salt/modules/dockermod.py
index 176b4db926..cad307e7af 100644
--- a/salt/modules/dockermod.py
+++ b/salt/modules/dockermod.py
@@ -217,7 +217,6 @@ import re
 import shutil
 import string
 import subprocess
-import sys
 import time
 import uuid
 
@@ -6865,9 +6864,32 @@ def call(name, function, *args, **kwargs):
         name, thin_path, os.path.join(thin_dest_path, 
os.path.basename(thin_path))
     )
 
+    # figure out available python interpreter inside the container
+    pycmds = (
+        "python3",
+        "/usr/libexec/platform-python",
+        "python27",
+        "python2.7",
+        "python26",
+        "python2.6",
+        "python2",
+        "python",
+    )
+    container_python_bin = None
+    for py_cmd in pycmds:
+        cmd = [py_cmd] + ["--version"]
+        ret = run_all(name, subprocess.list2cmdline(cmd))
+        if ret["retcode"] == 0:
+            container_python_bin = py_cmd
+            break
+    if not container_python_bin:
+        raise CommandExecutionError(
+            "Python interpreter cannot be found inside the container. Make 
sure Python is installed in the container"
+        )
+
     # untar archive
     untar_cmd = [
-        "python",
+        container_python_bin,
         "-c",
         ("import tarfile; " 
'tarfile.open("{0}/{1}").extractall(path="{0}")').format(
             thin_dest_path, os.path.basename(thin_path)
@@ -6880,7 +6902,7 @@ def call(name, function, *args, **kwargs):
     try:
         salt_argv = (
             [
-                "python{0}".format(sys.version_info[0]),
+                container_python_bin,
                 os.path.join(thin_dest_path, "salt-call"),
                 "--metadata",
                 "--local",
diff --git a/tests/unit/modules/test_dockermod.py 
b/tests/unit/modules/test_dockermod.py
index 48526acb71..ebe97a83f5 100644
--- a/tests/unit/modules/test_dockermod.py
+++ b/tests/unit/modules/test_dockermod.py
@@ -1049,33 +1049,35 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
         # [ call(name, [args]), ...
         self.maxDiff = None
         self.assertIn("mkdir", docker_run_all_mock.mock_calls[0][1][1])
-        self.assertIn("mkdir", docker_run_all_mock.mock_calls[4][1][1])
+        self.assertIn("mkdir", docker_run_all_mock.mock_calls[5][1][1])
         self.assertNotEqual(
             docker_run_all_mock.mock_calls[0][1][1],
-            docker_run_all_mock.mock_calls[4][1][1],
+            docker_run_all_mock.mock_calls[5][1][1],
         )
 
-        self.assertIn("salt-call", docker_run_all_mock.mock_calls[2][1][1])
-        self.assertIn("salt-call", docker_run_all_mock.mock_calls[6][1][1])
+        self.assertEqual("python3 --version", 
docker_run_all_mock.mock_calls[1][1][1])
+
+        self.assertIn("salt-call", docker_run_all_mock.mock_calls[3][1][1])
+        self.assertIn("salt-call", docker_run_all_mock.mock_calls[8][1][1])
         self.assertNotEqual(
-            docker_run_all_mock.mock_calls[2][1][1],
-            docker_run_all_mock.mock_calls[6][1][1],
+            docker_run_all_mock.mock_calls[3][1][1],
+            docker_run_all_mock.mock_calls[8][1][1],
         )
 
         # check thin untar
-        self.assertIn("tarfile", docker_run_all_mock.mock_calls[1][1][1])
-        self.assertIn("tarfile", docker_run_all_mock.mock_calls[5][1][1])
+        self.assertIn("tarfile", docker_run_all_mock.mock_calls[2][1][1])
+        self.assertIn("tarfile", docker_run_all_mock.mock_calls[7][1][1])
         self.assertNotEqual(
-            docker_run_all_mock.mock_calls[1][1][1],
-            docker_run_all_mock.mock_calls[5][1][1],
+            docker_run_all_mock.mock_calls[2][1][1],
+            docker_run_all_mock.mock_calls[7][1][1],
         )
 
         # check directory cleanup
-        self.assertIn("rm -rf", docker_run_all_mock.mock_calls[3][1][1])
-        self.assertIn("rm -rf", docker_run_all_mock.mock_calls[7][1][1])
+        self.assertIn("rm -rf", docker_run_all_mock.mock_calls[4][1][1])
+        self.assertIn("rm -rf", docker_run_all_mock.mock_calls[9][1][1])
         self.assertNotEqual(
-            docker_run_all_mock.mock_calls[3][1][1],
-            docker_run_all_mock.mock_calls[7][1][1],
+            docker_run_all_mock.mock_calls[4][1][1],
+            docker_run_all_mock.mock_calls[9][1][1],
         )
 
         self.assertEqual({"retcode": 0, "comment": "container cmd"}, ret)
-- 
2.31.1


++++++ fix-error-handling-in-openscap-module-bsc-1188647-40.patch ++++++
>From b7d11d8caf3eb4fb39a070201be87bb1b3abd525 Mon Sep 17 00:00:00 2001
From: Vladimir Nadvornik <nadvor...@suse.cz>
Date: Wed, 11 Aug 2021 12:19:09 +0200
Subject: [PATCH] Fix error handling in openscap module (bsc#1188647)
 (#409)

---
 salt/modules/openscap.py | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/salt/modules/openscap.py b/salt/modules/openscap.py
index f75e1c5e6b..216fd89eef 100644
--- a/salt/modules/openscap.py
+++ b/salt/modules/openscap.py
@@ -153,7 +153,9 @@ def xccdf_eval(xccdffile, ovalfiles=None, **kwargs):
         tempdir = tempfile.mkdtemp()
         proc = Popen(cmd_opts, stdout=PIPE, stderr=PIPE, cwd=tempdir)
         (stdoutdata, error) = proc.communicate()
-        success = _OSCAP_EXIT_CODES_MAP[proc.returncode]
+        success = _OSCAP_EXIT_CODES_MAP.get(proc.returncode, False)
+        if proc.returncode < 0:
+            error += "\nKilled by signal 
{}\n".format(proc.returncode).encode('ascii')
         returncode = proc.returncode
         if success:
             __salt__["cp.push_dir"](tempdir)
@@ -202,7 +204,9 @@ def xccdf(params):
         tempdir = tempfile.mkdtemp()
         proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, cwd=tempdir)
         (stdoutdata, error) = proc.communicate()
-        success = _OSCAP_EXIT_CODES_MAP[proc.returncode]
+        success = _OSCAP_EXIT_CODES_MAP.get(proc.returncode, False)
+        if proc.returncode < 0:
+            error += "\nKilled by signal 
{}\n".format(proc.returncode).encode('ascii')
         returncode = proc.returncode
         if success:
             __salt__["cp.push_dir"](tempdir)
-- 
2.32.0


++++++ fix-exception-in-yumpkg.remove-for-not-installed-pac.patch ++++++
>From 30a2c8c042f0fe57253a8ab47220d897bc89bd17 Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com>
Date: Thu, 24 Jun 2021 13:17:13 +0300
Subject: [PATCH] Fix exception in yumpkg.remove for not installed
 package (#380)

---
 salt/modules/yumpkg.py            |  2 ++
 tests/unit/modules/test_yumpkg.py | 25 +++++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py
index 0fb41a0400..c9f9f2c2d3 100644
--- a/salt/modules/yumpkg.py
+++ b/salt/modules/yumpkg.py
@@ -2051,6 +2051,8 @@ def remove(name=None, pkgs=None, **kwargs):  # pylint: 
disable=W0613
     old = list_pkgs()
     targets = []
     for target in pkg_params:
+        if target not in old:
+            continue
         version_to_remove = pkg_params[target]
         installed_versions = old[target].split(",")
 
diff --git a/tests/unit/modules/test_yumpkg.py 
b/tests/unit/modules/test_yumpkg.py
index e22c0b9251..373d2e09cb 100644
--- a/tests/unit/modules/test_yumpkg.py
+++ b/tests/unit/modules/test_yumpkg.py
@@ -1099,6 +1099,31 @@ class YumTestCase(TestCase, LoaderModuleMockMixin):
                 call = cmd_mock.mock_calls[0][1][0]
                 assert call == expected, call
 
+    def test_remove_not_existing(self):
+        """
+        Test if no exception on removing not installed package
+        """
+        name = "foo"
+        def list_pkgs_mock():
+            return {}
+        cmd_mock = MagicMock(
+            return_value={"pid": 12345, "retcode": 0, "stdout": "", "stderr": 
""}
+        )
+        salt_mock = {
+            "cmd.run_all": cmd_mock,
+            "lowpkg.version_cmp": rpm.version_cmp,
+            "pkg_resource.parse_targets": MagicMock(
+                return_value=({name: None}, "repository")
+            ),
+        }
+        with patch.object(yumpkg, "list_pkgs", list_pkgs_mock), patch(
+            "salt.utils.systemd.has_scope", MagicMock(return_value=False)
+        ), patch.dict(yumpkg.__salt__, salt_mock):
+
+            with patch.dict(yumpkg.__grains__, {"os": "CentOS", "osrelease": 
7}):
+                yumpkg.remove(name)
+                cmd_mock.assert_not_called()
+
     def test_install_with_epoch(self):
         """
         Tests that we properly identify a version containing an epoch as an
-- 
2.32.0


++++++ fix-failing-unit-tests-for-systemd.patch ++++++
>From 74d8f5f2d896e5e8bbf7d3fb614ae32f2cf489a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernan...@suse.com>
Date: Wed, 11 Aug 2021 11:44:54 +0100
Subject: [PATCH] Fix failing unit tests for systemd

---
 tests/unit/modules/test_systemd_service.py | 24 ++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/tests/unit/modules/test_systemd_service.py 
b/tests/unit/modules/test_systemd_service.py
index bbd89bb3d0..51be130d29 100644
--- a/tests/unit/modules/test_systemd_service.py
+++ b/tests/unit/modules/test_systemd_service.py
@@ -165,21 +165,27 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin):
 
         # systemd < 231
         with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 
230}):
-            with patch.object(systemd, "_systemctl_status", mock):
+            with patch.object(systemd, "_systemctl_status", mock), 
patch.object(
+                systemd, "offline", MagicMock(return_value=False)
+            ):
                 self.assertTrue(systemd.available("sshd.service"))
                 self.assertFalse(systemd.available("foo.service"))
 
         # systemd >= 231
         with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 
231}):
             with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
-                with patch.object(systemd, "_systemctl_status", mock):
+                with patch.object(systemd, "_systemctl_status", mock), 
patch.object(
+                    systemd, "offline", MagicMock(return_value=False)
+                ):
                     self.assertTrue(systemd.available("sshd.service"))
                     self.assertFalse(systemd.available("bar.service"))
 
         # systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3)
         with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 
219}):
             with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
-                with patch.object(systemd, "_systemctl_status", mock):
+                with patch.object(systemd, "_systemctl_status", mock), 
patch.object(
+                    systemd, "offline", MagicMock(return_value=False)
+                ):
                     self.assertTrue(systemd.available("sshd.service"))
                     self.assertFalse(systemd.available("bar.service"))
 
@@ -191,21 +197,27 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin):
 
         # systemd < 231
         with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 
230}):
-            with patch.object(systemd, "_systemctl_status", mock):
+            with patch.object(systemd, "_systemctl_status", mock), 
patch.object(
+                systemd, "offline", MagicMock(return_value=False)
+            ):
                 self.assertFalse(systemd.missing("sshd.service"))
                 self.assertTrue(systemd.missing("foo.service"))
 
         # systemd >= 231
         with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 
231}):
             with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
-                with patch.object(systemd, "_systemctl_status", mock):
+                with patch.object(systemd, "_systemctl_status", mock), 
patch.object(
+                    systemd, "offline", MagicMock(return_value=False)
+                ):
                     self.assertFalse(systemd.missing("sshd.service"))
                     self.assertTrue(systemd.missing("bar.service"))
 
         # systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3)
         with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 
219}):
             with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
-                with patch.object(systemd, "_systemctl_status", mock):
+                with patch.object(systemd, "_systemctl_status", mock), 
patch.object(
+                    systemd, "offline", MagicMock(return_value=False)
+                ):
                     self.assertFalse(systemd.missing("sshd.service"))
                     self.assertTrue(systemd.missing("bar.service"))
 
-- 
2.32.0


++++++ fix-issue-parsing-errors-in-ansiblegate-state-module.patch ++++++
>From cc017f6ed279af7fe02c890e4a7725e6903f364d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernan...@suse.com>
Date: Mon, 26 Apr 2021 12:13:59 +0100
Subject: [PATCH] Fix issue parsing errors in ansiblegate state module

---
 salt/states/ansiblegate.py | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/salt/states/ansiblegate.py b/salt/states/ansiblegate.py
index 5daba0f37f..bd00653928 100644
--- a/salt/states/ansiblegate.py
+++ b/salt/states/ansiblegate.py
@@ -183,7 +183,11 @@ def playbooks(name, rundir=None, git_repo=None, 
git_kwargs=None, ansible_kwargs=
         checks = __salt__["ansible.playbooks"](
             name, rundir=rundir, check=True, diff=True, **ansible_kwargs
         )
-        if all(
+        if "stats" not in checks:
+            ret["comment"] = checks.get("stderr", checks)
+            ret["result"] = False
+            ret["changes"] = {}
+        elif all(
             not check["changed"]
             and not check["failures"]
             and not check["unreachable"]
@@ -212,7 +216,11 @@ def playbooks(name, rundir=None, git_repo=None, 
git_kwargs=None, ansible_kwargs=
         results = __salt__["ansible.playbooks"](
             name, rundir=rundir, diff=True, **ansible_kwargs
         )
-        if all(
+        if "stats" not in results:
+            ret["comment"] = results.get("stderr", results)
+            ret["result"] = False
+            ret["changes"] = {}
+        elif all(
             not check["changed"]
             and not check["failures"]
             and not check["unreachable"]
-- 
2.31.1


++++++ fix-missing-minion-returns-in-batch-mode-360.patch ++++++
>From 83fbfcbf49c98624029f1d215b7ad4d247128d39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Hole=C4=8Dek?= <ohole...@aaannz.eu>
Date: Mon, 10 May 2021 16:23:19 +0200
Subject: [PATCH] Fix missing minion returns in batch mode (#360)

Don't close pub if there are pending events, otherwise events will be lost
resulting in empty minion returns.

Co-authored-by: Denis V. Meltsaykin <dmeltsay...@mirantis.com>
---
 salt/client/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/salt/client/__init__.py b/salt/client/__init__.py
index ddb437604b..78f4d99e84 100644
--- a/salt/client/__init__.py
+++ b/salt/client/__init__.py
@@ -920,7 +920,7 @@ class LocalClient:
 
                 self._clean_up_subscriptions(pub_data["jid"])
         finally:
-            if not was_listening:
+            if not was_listening and not self.event.pending_events:
                 self.event.close_pub()
 
     def cmd_full_return(
-- 
2.31.1


++++++ fix-save-for-iptables-state-module-bsc-1185131-372.patch ++++++
>From 944f2a8e4db522ad32f547cf350a1268caa6de5a Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com>
Date: Thu, 24 Jun 2021 13:18:51 +0300
Subject: [PATCH] Fix save for iptables state module (bsc#1185131)
 (#372)

---
 salt/states/iptables.py            |  86 ++++++++------
 tests/unit/states/test_iptables.py | 184 ++++++++++++++++++++++++++++-
 2 files changed, 227 insertions(+), 43 deletions(-)

diff --git a/salt/states/iptables.py b/salt/states/iptables.py
index 61dfc7e665..2e81477f18 100644
--- a/salt/states/iptables.py
+++ b/salt/states/iptables.py
@@ -401,7 +401,7 @@ def append(name, table="filter", family="ipv4", **kwargs):
         if save:
             if save_file is True:
                 save_file = None
-            __salt__["iptables.save"](save_file, family=family)
+            __salt__["iptables.save"](filename=save_file, family=family)
         if not ret["changes"]["locale"]:
             del ret["changes"]["locale"]
         ret["comment"] = "\n".join(comments)
@@ -426,7 +426,9 @@ def append(name, table="filter", family="ipv4", **kwargs):
                 filename = kwargs["save"]
             else:
                 filename = None
-            saved_rules = __salt__["iptables.get_saved_rules"](family=family)
+            saved_rules = __salt__["iptables.get_saved_rules"](
+                conf_file=filename, family=family
+            )
             _rules = __salt__["iptables.get_rules"](family=family)
             __rules = []
             for table in _rules:
@@ -438,7 +440,7 @@ def append(name, table="filter", family="ipv4", **kwargs):
                     
__saved_rules.append(saved_rules[table][chain].get("rules"))
             # Only save if rules in memory are different than saved rules
             if __rules != __saved_rules:
-                out = __salt__["iptables.save"](filename, family=family)
+                out = __salt__["iptables.save"](filename=filename, 
family=family)
                 ret["comment"] += ("\nSaved iptables rule {} for {}\n" 
"{}\n{}").format(
                     name, family, command.strip(), out
                 )
@@ -454,16 +456,15 @@ def append(name, table="filter", family="ipv4", **kwargs):
         ret["comment"] = "Set iptables rule for {} to: {} for {}".format(
             name, command.strip(), family
         )
-        if "save" in kwargs:
-            if kwargs["save"]:
-                if kwargs["save"] is not True:
-                    filename = kwargs["save"]
-                else:
-                    filename = None
-                out = __salt__["iptables.save"](filename, family=family)
-                ret["comment"] = (
-                    "Set and saved iptables rule {} for {}\n" "{}\n{}"
-                ).format(name, family, command.strip(), out)
+        if "save" in kwargs and kwargs["save"]:
+            if kwargs["save"] is not True:
+                filename = kwargs["save"]
+            else:
+                filename = None
+            out = __salt__["iptables.save"](filename=filename, family=family)
+            ret["comment"] = (
+                "Set and saved iptables rule {} for {}\n" "{}\n{}"
+            ).format(name, family, command.strip(), out)
         return ret
     else:
         ret["result"] = False
@@ -527,7 +528,7 @@ def insert(name, table="filter", family="ipv4", **kwargs):
         if save:
             if save_file is True:
                 save_file = None
-            __salt__["iptables.save"](save_file, family=family)
+            __salt__["iptables.save"](filename=save_file, family=family)
         if not ret["changes"]["locale"]:
             del ret["changes"]["locale"]
         ret["comment"] = "\n".join(comments)
@@ -552,7 +553,9 @@ def insert(name, table="filter", family="ipv4", **kwargs):
                 filename = kwargs["save"]
             else:
                 filename = None
-            saved_rules = __salt__["iptables.get_saved_rules"](family=family)
+            saved_rules = __salt__["iptables.get_saved_rules"](
+                conf_file=filename, family=family
+            )
             _rules = __salt__["iptables.get_rules"](family=family)
             __rules = []
             for table in _rules:
@@ -564,7 +567,7 @@ def insert(name, table="filter", family="ipv4", **kwargs):
                     
__saved_rules.append(saved_rules[table][chain].get("rules"))
             # Only save if rules in memory are different than saved rules
             if __rules != __saved_rules:
-                out = __salt__["iptables.save"](filename, family=family)
+                out = __salt__["iptables.save"](filename=filename, 
family=family)
                 ret["comment"] += ("\nSaved iptables rule {} for {}\n" 
"{}\n{}").format(
                     name, family, command.strip(), out
                 )
@@ -582,12 +585,15 @@ def insert(name, table="filter", family="ipv4", **kwargs):
         ret["comment"] = "Set iptables rule for {} to: {} for {}".format(
             name, command.strip(), family
         )
-        if "save" in kwargs:
-            if kwargs["save"]:
-                out = __salt__["iptables.save"](filename=None, family=family)
-                ret["comment"] = (
-                    "Set and saved iptables rule {} for {}\n" "{}\n{}"
-                ).format(name, family, command.strip(), out)
+        if "save" in kwargs and kwargs["save"]:
+            if kwargs["save"] is not True:
+                filename = kwargs["save"]
+            else:
+                filename = None
+            out = __salt__["iptables.save"](filename=filename, family=family)
+            ret["comment"] = (
+                "Set and saved iptables rule {} for {}\n" "{}\n{}"
+            ).format(name, family, command.strip(), out)
         return ret
     else:
         ret["result"] = False
@@ -646,7 +652,7 @@ def delete(name, table="filter", family="ipv4", **kwargs):
         if save:
             if save_file is True:
                 save_file = None
-            __salt__["iptables.save"](save_file, family=family)
+            __salt__["iptables.save"](filename=save_file, family=family)
         if not ret["changes"]["locale"]:
             del ret["changes"]["locale"]
         ret["comment"] = "\n".join(comments)
@@ -688,12 +694,15 @@ def delete(name, table="filter", family="ipv4", **kwargs):
         ret["changes"] = {"locale": name}
         ret["result"] = True
         ret["comment"] = "Delete iptables rule for {} {}".format(name, 
command.strip())
-        if "save" in kwargs:
-            if kwargs["save"]:
-                out = __salt__["iptables.save"](filename=None, family=family)
-                ret["comment"] = (
-                    "Deleted and saved iptables rule {} for {}\n" "{}\n{}"
-                ).format(name, family, command.strip(), out)
+        if "save" in kwargs and kwargs["save"]:
+            if kwargs["save"] is not True:
+                filename = kwargs["save"]
+            else:
+                filename = None
+            out = __salt__["iptables.save"](filename=filename, family=family)
+            ret["comment"] = (
+                "Deleted and saved iptables rule {} for {}\n" "{}\n{}"
+            ).format(name, family, command.strip(), out)
         return ret
     else:
         ret["result"] = False
@@ -751,14 +760,17 @@ def set_policy(name, table="filter", family="ipv4", 
**kwargs):
         ret["comment"] = "Set default policy for {} to {} family {}".format(
             kwargs["chain"], kwargs["policy"], family
         )
-        if "save" in kwargs:
-            if kwargs["save"]:
-                __salt__["iptables.save"](filename=None, family=family)
-                ret[
-                    "comment"
-                ] = "Set and saved default policy for {} to {} family 
{}".format(
-                    kwargs["chain"], kwargs["policy"], family
-                )
+        if "save" in kwargs and kwargs["save"]:
+            if kwargs["save"] is not True:
+                filename = kwargs["save"]
+            else:
+                filename = None
+            __salt__["iptables.save"](filename=filename, family=family)
+            ret[
+                "comment"
+            ] = "Set and saved default policy for {} to {} family {}".format(
+                kwargs["chain"], kwargs["policy"], family
+            )
         return ret
     else:
         ret["result"] = False
diff --git a/tests/unit/states/test_iptables.py 
b/tests/unit/states/test_iptables.py
index c49022c962..975ae49c3e 100644
--- a/tests/unit/states/test_iptables.py
+++ b/tests/unit/states/test_iptables.py
@@ -135,7 +135,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
         with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock):
             mock = MagicMock(return_value="a")
             with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}):
-                mock = MagicMock(side_effect=[True, False, False, False])
+                mock = MagicMock(side_effect=[True, False, False, False, 
False, True])
                 with patch.dict(iptables.__salt__, {"iptables.check": mock}):
                     ret.update(
                         {
@@ -161,7 +161,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
                         )
 
                     with patch.dict(iptables.__opts__, {"test": False}):
-                        mock = MagicMock(side_effect=[True, False])
+                        mock = MagicMock(side_effect=[True, False, True, True])
                         with patch.dict(iptables.__salt__, {"iptables.append": 
mock}):
                             ret.update(
                                 {
@@ -188,6 +188,65 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
                                 iptables.append("salt", table="", chain=""), 
ret
                             )
 
+                            mock_save = MagicMock(
+                                side_effect=['Wrote 1 lines to 
"/tmp/iptables"', ""]
+                            )
+                            with patch.dict(
+                                iptables.__salt__, {"iptables.save": mock_save}
+                            ):
+                                mock_get_saved_rules = 
MagicMock(side_effect=[""])
+                                with patch.dict(
+                                    iptables.__salt__,
+                                    {"iptables.get_saved_rules": 
mock_get_saved_rules},
+                                ):
+                                    mock = MagicMock(side_effect=[""])
+                                    with patch.dict(
+                                        iptables.__salt__, 
{"iptables.get_rules": mock}
+                                    ):
+                                        ret.update(
+                                            {
+                                                "changes": {"locale": "salt"},
+                                                "result": True,
+                                                "comment": "Set and saved 
iptables rule"
+                                                ' salt for ipv4\na\nWrote 1 
lines to "/tmp/iptables"',
+                                            }
+                                        )
+                                        self.assertDictEqual(
+                                            iptables.append(
+                                                "salt",
+                                                table="",
+                                                chain="",
+                                                save="/tmp/iptables",
+                                            ),
+                                            ret,
+                                        )
+                                        ret.update(
+                                            {
+                                                "changes": {},
+                                                "result": True,
+                                                "comment": "iptables rule for 
salt already set (a) for ipv4",
+                                            }
+                                        )
+                                        self.assertDictEqual(
+                                            iptables.append(
+                                                "salt",
+                                                table="",
+                                                chain="",
+                                                save="/tmp/iptables",
+                                            ),
+                                            ret,
+                                        )
+                                        self.assertEqual(
+                                            
mock_get_saved_rules.mock_calls[0][2][
+                                                "conf_file"
+                                            ],
+                                            "/tmp/iptables",
+                                        )
+                                        self.assertEqual(
+                                            
mock_save.mock_calls[0][2]["filename"],
+                                            "/tmp/iptables",
+                                        )
+
     def test_insert(self):
         """
             Test to insert a rule into a chain
@@ -200,7 +259,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
         with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock):
             mock = MagicMock(return_value="a")
             with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}):
-                mock = MagicMock(side_effect=[True, False, False, False])
+                mock = MagicMock(side_effect=[True, False, False, False, 
False, True])
                 with patch.dict(iptables.__salt__, {"iptables.check": mock}):
                     ret.update(
                         {
@@ -226,7 +285,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
                         )
 
                     with patch.dict(iptables.__opts__, {"test": False}):
-                        mock = MagicMock(side_effect=[False, True])
+                        mock = MagicMock(side_effect=[False, True, False, 
True])
                         with patch.dict(iptables.__salt__, {"iptables.insert": 
mock}):
                             ret.update(
                                 {
@@ -258,6 +317,67 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
                                 ret,
                             )
 
+                            mock_save = MagicMock(
+                                side_effect=['Wrote 1 lines to 
"/tmp/iptables"', ""]
+                            )
+                            with patch.dict(
+                                iptables.__salt__, {"iptables.save": mock_save}
+                            ):
+                                mock_get_saved_rules = 
MagicMock(side_effect=[""])
+                                with patch.dict(
+                                    iptables.__salt__,
+                                    {"iptables.get_saved_rules": 
mock_get_saved_rules},
+                                ):
+                                    mock = MagicMock(side_effect=[""])
+                                    with patch.dict(
+                                        iptables.__salt__, 
{"iptables.get_rules": mock}
+                                    ):
+                                        ret.update(
+                                            {
+                                                "changes": {"locale": "salt"},
+                                                "result": True,
+                                                "comment": "Set and saved 
iptables rule"
+                                                ' salt for ipv4\na\nWrote 1 
lines to "/tmp/iptables"',
+                                            }
+                                        )
+                                        self.assertDictEqual(
+                                            iptables.insert(
+                                                "salt",
+                                                table="",
+                                                chain="",
+                                                position="",
+                                                save="/tmp/iptables",
+                                            ),
+                                            ret,
+                                        )
+                                        ret.update(
+                                            {
+                                                "changes": {},
+                                                "result": True,
+                                                "comment": "iptables rule for 
salt already set for ipv4 (a)",
+                                            }
+                                        )
+                                        self.assertDictEqual(
+                                            iptables.insert(
+                                                "salt",
+                                                table="",
+                                                chain="",
+                                                position="",
+                                                save="/tmp/iptables",
+                                            ),
+                                            ret,
+                                        )
+                                        self.assertEqual(
+                                            
mock_get_saved_rules.mock_calls[0][2][
+                                                "conf_file"
+                                            ],
+                                            "/tmp/iptables",
+                                        )
+                                        self.assertEqual(
+                                            
mock_save.mock_calls[0][2]["filename"],
+                                            "/tmp/iptables",
+                                        )
+
     def test_delete(self):
         """
             Test to delete a rule to a chain
@@ -270,7 +390,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
         with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock):
             mock = MagicMock(return_value="a")
             with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}):
-                mock = MagicMock(side_effect=[False, True, True, True])
+                mock = MagicMock(side_effect=[False, True, True, True, True, 
False])
                 with patch.dict(iptables.__salt__, {"iptables.check": mock}):
                     ret.update(
                         {
@@ -296,7 +416,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
                         )
 
                     with patch.dict(iptables.__opts__, {"test": False}):
-                        mock = MagicMock(side_effect=[False, True])
+                        mock = MagicMock(side_effect=[False, True, False, 
False])
                         with patch.dict(iptables.__salt__, {"iptables.delete": 
mock}):
                             ret.update(
                                 {
@@ -327,6 +447,58 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
                                 ret,
                             )
 
+                            mock_save = MagicMock(
+                                side_effect=['Wrote 1 lines to 
"/tmp/iptables"', ""]
+                            )
+                            with patch.dict(
+                                iptables.__salt__, {"iptables.save": mock_save}
+                            ):
+                                mock = MagicMock(side_effect=[True, False])
+                                with patch.dict(
+                                    iptables.__salt__, {"iptables.check": mock}
+                                ):
+                                    mock = MagicMock(side_effect=[""])
+                                    with patch.dict(
+                                        iptables.__salt__, 
{"iptables.get_rules": mock}
+                                    ):
+                                        ret.update(
+                                            {
+                                                "changes": {"locale": "salt"},
+                                                "result": True,
+                                                "comment": "Deleted and saved 
iptables rule"
+                                                ' salt for ipv4\na\nWrote 1 
lines to "/tmp/iptables"',
+                                            }
+                                        )
+                                        self.assertDictEqual(
+                                            iptables.delete(
+                                                "salt",
+                                                table="",
+                                                chain="",
+                                                save="/tmp/iptables",
+                                            ),
+                                            ret,
+                                        )
+                                        ret.update(
+                                            {
+                                                "changes": {},
+                                                "result": True,
+                                                "comment": "iptables rule for 
salt already absent for ipv4 (a)",
+                                            }
+                                        )
+                                        self.assertDictEqual(
+                                            iptables.delete(
+                                                "salt",
+                                                table="",
+                                                chain="",
+                                                save="/tmp/iptables",
+                                            ),
+                                            ret,
+                                        )
+                                        self.assertEqual(
+                                            
mock_save.mock_calls[0][2]["filename"],
+                                            "/tmp/iptables",
+                                        )
+
     def test_set_policy(self):
         """
             Test to sets the default policy for iptables firewall tables
-- 
2.32.0


++++++ grains.extra-support-old-non-intel-kernels-bsc-11806.patch ++++++
>From 27c7a9f62b1a589365785c9428293653ac76fee3 Mon Sep 17 00:00:00 2001
From: Alberto Planas <apla...@suse.com>
Date: Mon, 10 May 2021 16:26:02 +0200
Subject: [PATCH] grains.extra: support old non-intel kernels
 (bsc#1180650) (#368)

---
 salt/grains/extra.py | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/salt/grains/extra.py b/salt/grains/extra.py
index 7729a5c0a5..f2abd1281c 100644
--- a/salt/grains/extra.py
+++ b/salt/grains/extra.py
@@ -71,10 +71,10 @@ def suse_backported_capabilities():
     }
 
 
-def __secure_boot():
+def __secure_boot(efivars_dir):
     """Detect if secure-boot is enabled."""
     enabled = False
-    sboot = glob.glob("/sys/firmware/efi/vars/SecureBoot-*/data")
+    sboot = glob.glob(os.path.join(efivars_dir, "SecureBoot-*/data"))
     if len(sboot) == 1:
         # The minion is usually running as a privileged user, but is
         # not the case for the master.  Seems that the master can also
@@ -89,9 +89,17 @@ def __secure_boot():
 
 def uefi():
     """Populate UEFI grains."""
+    efivars_dir = next(
+        iter(
+            filter(
+                os.path.exists, ["/sys/firmware/efi/efivars", 
"/sys/firmware/efi/vars"]
+            )
+        ),
+        None,
+    )
     grains = {
-        "efi": os.path.exists("/sys/firmware/efi/systab"),
-        "efi-secure-boot": __secure_boot(),
+        "efi": bool(efivars_dir),
+        "efi-secure-boot": __secure_boot(efivars_dir) if efivars_dir else 
False,
     }
 
     return grains
-- 
2.31.1


++++++ handle-master-tops-data-when-states-are-applied-by-t.patch ++++++
>From e0b7511e30da289b4100aa156b67b652681afc03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernan...@suse.com>
Date: Thu, 8 Jul 2021 08:57:13 +0100
Subject: [PATCH] Handle "master tops" data when states are applied by
 "transactional_update" (bsc#1187787) (#398)

* Handle master tops data when states are applied by transactional_update 
(bsc#1187787)

* Fix unit tests for transactional_update module
---
 salt/modules/transactional_update.py          |  9 +++++++--
 .../unit/modules/test_transactional_update.py | 20 +++++++++----------
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/salt/modules/transactional_update.py 
b/salt/modules/transactional_update.py
index 7bbdb697b8..9cdaddb91a 100644
--- a/salt/modules/transactional_update.py
+++ b/salt/modules/transactional_update.py
@@ -301,6 +301,11 @@ def __virtual__():
         return (False, "Module transactional_update requires a transactional 
system")
 
 
+class TransactionalUpdateHighstate(salt.client.ssh.state.SSHHighState):
+    def _master_tops(self):
+        return self.client.master_tops()
+
+
 def _global_params(self_update, snapshot=None, quiet=False):
     """Utility function to prepare common global parameters."""
     params = ["--non-interactive", "--drop-if-no-change"]
@@ -1107,7 +1112,7 @@ def sls(
     # Clone the options data and apply some default values. May not be
     # needed, as this module just delegate
     opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
-    st_ = salt.client.ssh.state.SSHHighState(
+    st_ = TransactionalUpdateHighstate(
         opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__)
     )
 
@@ -1180,7 +1185,7 @@ def highstate(activate_transaction=False, **kwargs):
     # Clone the options data and apply some default values. May not be
     # needed, as this module just delegate
     opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
-    st_ = salt.client.ssh.state.SSHHighState(
+    st_ = TransactionalUpdateHighstate(
         opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__)
     )
 
diff --git a/tests/unit/modules/test_transactional_update.py 
b/tests/unit/modules/test_transactional_update.py
index 19e477d02f..2d30f296d7 100644
--- a/tests/unit/modules/test_transactional_update.py
+++ b/tests/unit/modules/test_transactional_update.py
@@ -622,22 +622,22 @@ class TransactionalUpdateTestCase(TestCase, 
LoaderModuleMockMixin):
             utils_mock["files.rm_rf"].assert_called_once()
 
     @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.client.ssh.state.SSHHighState")
+    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
     @patch("salt.fileclient.get_file_client")
     @patch("salt.utils.state.get_sls_opts")
     def test_sls(
         self,
         get_sls_opts,
         get_file_client,
-        SSHHighState,
+        TransactionalUpdateHighstate,
         _create_and_execute_salt_state,
     ):
         """Test transactional_update.sls"""
-        SSHHighState.return_value = SSHHighState
-        SSHHighState.render_highstate.return_value = (None, [])
-        SSHHighState.state.reconcile_extend.return_value = (None, [])
-        SSHHighState.state.requisite_in.return_value = (None, [])
-        SSHHighState.state.verify_high.return_value = []
+        TransactionalUpdateHighstate.return_value = 
TransactionalUpdateHighstate
+        TransactionalUpdateHighstate.render_highstate.return_value = (None, [])
+        TransactionalUpdateHighstate.state.reconcile_extend.return_value = 
(None, [])
+        TransactionalUpdateHighstate.state.requisite_in.return_value = (None, 
[])
+        TransactionalUpdateHighstate.state.verify_high.return_value = []
 
         _create_and_execute_salt_state.return_value = "result"
         opts_mock = {
@@ -649,18 +649,18 @@ class TransactionalUpdateTestCase(TestCase, 
LoaderModuleMockMixin):
             _create_and_execute_salt_state.assert_called_once()
 
     @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.client.ssh.state.SSHHighState")
+    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
     @patch("salt.fileclient.get_file_client")
     @patch("salt.utils.state.get_sls_opts")
     def test_highstate(
         self,
         get_sls_opts,
         get_file_client,
-        SSHHighState,
+        TransactionalUpdateHighstate,
         _create_and_execute_salt_state,
     ):
         """Test transactional_update.highstage"""
-        SSHHighState.return_value = SSHHighState
+        TransactionalUpdateHighstate.return_value = 
TransactionalUpdateHighstate
 
         _create_and_execute_salt_state.return_value = "result"
         opts_mock = {
-- 
2.32.0


++++++ handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch ++++++
>From b154f0a17c85c2fe0b85226dfeb3919bd833a85c Mon Sep 17 00:00:00 2001
From: Cedric Bosdonnat <cedric.bosdon...@free.fr>
Date: Fri, 21 May 2021 13:04:46 +0200
Subject: [PATCH] Handle volumes on stopped pools in virt.vm_info
 (#373)

For VMs having at least a disk on a stopped volume, we don't want the
user to get an exception when running virt.vm_info. Instead just provide
less information.
---
 changelog/60132.fixed                         |  1 +
 salt/modules/virt.py                          | 73 +++++++++++--------
 .../pytests/unit/modules/virt/test_domain.py  |  9 ++-
 3 files changed, 50 insertions(+), 33 deletions(-)
 create mode 100644 changelog/60132.fixed

diff --git a/changelog/60132.fixed b/changelog/60132.fixed
new file mode 100644
index 0000000000..1e3bc96b98
--- /dev/null
+++ b/changelog/60132.fixed
@@ -0,0 +1 @@
+Gracefuly handle errors in virt.vm_info
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index 6409089109..d8a8c51ce5 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -515,41 +515,50 @@ def _get_disks(conn, dom):
             def _get_disk_volume_data(pool_name, volume_name):
                 qemu_target = "{}/{}".format(pool_name, volume_name)
                 pool = conn.storagePoolLookupByName(pool_name)
-                vol = pool.storageVolLookupByName(volume_name)
-                vol_info = vol.info()
-                extra_properties = {
-                    "virtual size": vol_info[1],
-                    "disk size": vol_info[2],
-                }
-
-                backing_files = [
-                    {
-                        "file": node.find("source").get("file"),
-                        "file format": node.find("format").get("type"),
+                extra_properties = {}
+                try:
+                    vol = pool.storageVolLookupByName(volume_name)
+                    vol_info = vol.info()
+                    extra_properties = {
+                        "virtual size": vol_info[1],
+                        "disk size": vol_info[2],
                     }
-                    for node in elem.findall(".//backingStore[source]")
-                ]
 
-                if backing_files:
-                    # We had the backing files in a flat list, nest them again.
-                    extra_properties["backing file"] = backing_files[0]
-                    parent = extra_properties["backing file"]
-                    for sub_backing_file in backing_files[1:]:
-                        parent["backing file"] = sub_backing_file
-                        parent = sub_backing_file
+                    backing_files = [
+                        {
+                            "file": node.find("source").get("file"),
+                            "file format": node.find("format").get("type"),
+                        }
+                        for node in elem.findall(".//backingStore[source]")
+                    ]
 
-                else:
-                    # In some cases the backing chain is not displayed by the 
domain definition
-                    # Try to see if we have some of it in the volume 
definition.
-                    vol_desc = ElementTree.fromstring(vol.XMLDesc())
-                    backing_path = vol_desc.find("./backingStore/path")
-                    backing_format = vol_desc.find("./backingStore/format")
-                    if backing_path is not None:
-                        extra_properties["backing file"] = {"file": 
backing_path.text}
-                        if backing_format is not None:
-                            extra_properties["backing file"][
-                                "file format"
-                            ] = backing_format.get("type")
+                    if backing_files:
+                        # We had the backing files in a flat list, nest them 
again.
+                        extra_properties["backing file"] = backing_files[0]
+                        parent = extra_properties["backing file"]
+                        for sub_backing_file in backing_files[1:]:
+                            parent["backing file"] = sub_backing_file
+                            parent = sub_backing_file
+
+                    else:
+                        # In some cases the backing chain is not displayed by 
the domain definition
+                        # Try to see if we have some of it in the volume 
definition.
+                        vol_desc = ElementTree.fromstring(vol.XMLDesc())
+                        backing_path = vol_desc.find("./backingStore/path")
+                        backing_format = vol_desc.find("./backingStore/format")
+                        if backing_path is not None:
+                            extra_properties["backing file"] = {
+                                "file": backing_path.text
+                            }
+                            if backing_format is not None:
+                                extra_properties["backing file"][
+                                    "file format"
+                                ] = backing_format.get("type")
+                except libvirt.libvirtError:
+                    # The volume won't be found if the pool is not started, 
just output less infos
+                    log.info(
+                        "Couldn't extract all volume informations: pool is 
likely not running or refreshed"
+                    )
                 return (qemu_target, extra_properties)
 
             if disk_type == "file":
diff --git a/tests/pytests/unit/modules/virt/test_domain.py 
b/tests/pytests/unit/modules/virt/test_domain.py
index 76433eaef4..a9453e4a66 100644
--- a/tests/pytests/unit/modules/virt/test_domain.py
+++ b/tests/pytests/unit/modules/virt/test_domain.py
@@ -192,6 +192,11 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool):
           <alias name='virtio-disk0'/>
           <address type='pci' domain='0x0000' bus='0x00' slot='0x04' 
function='0x0'/>
         </disk>
+        <disk type='volume' device='disk'>
+          <driver name='qemu' type='qcow2' cache='none' io='native'/>
+          <source pool='stopped' volume='vm05_data'/>
+          <target dev='vdd' bus='virtio'/>
+        </disk>
         <disk type='network' device='cdrom'>
           <driver name='qemu' type='raw' cache='none' io='native'/>
           <source protocol='http' name='/pub/iso/myimage.iso' 
query='foo=bar&amp;baz=flurb' index='1'>
@@ -205,11 +210,12 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool):
       </devices>
     </domain>
     """
-    domain_mock = make_mock_vm(vm_def)
+    make_mock_vm(vm_def)
 
     pool_mock = make_mock_storage_pool(
         "default", "dir", ["srv01_system", "srv01_data", "vm05_system"]
     )
+    make_mock_storage_pool("stopped", "dir", [])
 
     # Append backing store to srv01_data volume XML description
     srv1data_mock = pool_mock.storageVolLookupByName("srv01_data")
@@ -256,6 +262,7 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool):
                 },
             },
         },
+        "vdd": {"type": "disk", "file": "stopped/vm05_data", "file format": 
"qcow2"},
         "hda": {
             "type": "cdrom",
             "file format": "raw",
-- 
2.31.1


++++++ implementation-of-held-unheld-functions-for-state-pk.patch ++++++
++++ 903 lines (skipped)

++++++ move-vendor-change-logic-to-zypper-class-355.patch ++++++
++++ 843 lines (skipped)

++++++ parsing-epoch-out-of-version-provided-during-pkg-rem.patch ++++++
>From d1a8a0d724ee272953bb4615869d9fe468d28e98 Mon Sep 17 00:00:00 2001
From: Jochen Breuer <jbre...@suse.de>
Date: Mon, 3 May 2021 17:20:54 +0200
Subject: [PATCH] Parsing Epoch out of version provided during pkg
 remove (bsc#1173692)

yum doesn't seem to like the epoch information provided within the
version. Therefore it's removed before passing it to yum.

* Introducing `ignore_epoch` to pkg.remove
  Just like pkg.install pkg.remove now also has ignore_epoch. With
  this it is possible to ignore the epoch information completely
  during version comparison.
* No epoch regardless of arch
* Added tests for cases with and without arch.
* Epoch information is now skipped in all cases.
* Removes ignore_epoch from pkg state
---
 changelog/57881.changed           |  1 +
 salt/modules/yumpkg.py            | 14 +++--
 tests/unit/modules/test_yumpkg.py | 85 +++++++++++++++++++++++++++++++
 3 files changed, 96 insertions(+), 4 deletions(-)
 create mode 100644 changelog/57881.changed

diff --git a/changelog/57881.changed b/changelog/57881.changed
new file mode 100644
index 0000000000..e2ae2f4653
--- /dev/null
+++ b/changelog/57881.changed
@@ -0,0 +1 @@
+Parsing Epoch out of version during pkg remove, since yum can't handle that in 
all of the cases.
diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py
index 82adbbd59d..0fb41a0400 100644
--- a/salt/modules/yumpkg.py
+++ b/salt/modules/yumpkg.py
@@ -2051,11 +2051,13 @@ def remove(name=None, pkgs=None, **kwargs):  # pylint: 
disable=W0613
     old = list_pkgs()
     targets = []
     for target in pkg_params:
+        version_to_remove = pkg_params[target]
+        installed_versions = old[target].split(",")
+
         # Check if package version set to be removed is actually installed:
-        # old[target] contains a comma-separated list of installed versions
-        if target in old and not pkg_params[target]:
+        if target in old and not version_to_remove:
             targets.append(target)
-        elif target in old and pkg_params[target] in old[target].split(","):
+        elif target in old and version_to_remove in installed_versions:
             arch = ""
             pkgname = target
             try:
@@ -2066,7 +2068,11 @@ def remove(name=None, pkgs=None, **kwargs):  # pylint: 
disable=W0613
                 if archpart in salt.utils.pkg.rpm.ARCHES:
                     arch = "." + archpart
                     pkgname = namepart
-            targets.append("{}-{}{}".format(pkgname, pkg_params[target], arch))
+            # Since we don't always have the arch info, epoch information has 
to parsed out. But
+            # a version check was already performed, so we are removing the 
right version.
+            targets.append(
+                "{}-{}{}".format(pkgname, version_to_remove.split(":", 1)[-1], 
arch)
+            )
     if not targets:
         return {}
 
diff --git a/tests/unit/modules/test_yumpkg.py 
b/tests/unit/modules/test_yumpkg.py
index 96d3f12b17..e22c0b9251 100644
--- a/tests/unit/modules/test_yumpkg.py
+++ b/tests/unit/modules/test_yumpkg.py
@@ -1014,6 +1014,91 @@ class YumTestCase(TestCase, LoaderModuleMockMixin):
                     redirect_stderr=True,
                 )
 
+    def test_remove_with_epoch(self):
+        """
+        Tests that we properly identify a version containing an epoch for
+        deinstallation.
+
+        You can deinstall pkgs only without the epoch if no arch is provided:
+
+        .. code-block:: bash
+
+            yum remove PackageKit-yum-1.1.10-2.el7.centos
+        """
+        name = "foo"
+        installed = "8:3.8.12-4.n.el7"
+        list_pkgs_mock = MagicMock(
+            side_effect=lambda **kwargs: {
+                name: [installed]
+                if kwargs.get("versions_as_list", False)
+                else installed
+            }
+        )
+        cmd_mock = MagicMock(
+            return_value={"pid": 12345, "retcode": 0, "stdout": "", "stderr": 
""}
+        )
+        salt_mock = {
+            "cmd.run_all": cmd_mock,
+            "lowpkg.version_cmp": rpm.version_cmp,
+            "pkg_resource.parse_targets": MagicMock(
+                return_value=({name: installed}, "repository")
+            ),
+        }
+        full_pkg_string = "-".join((name, installed[2:]))
+        with patch.object(yumpkg, "list_pkgs", list_pkgs_mock), patch(
+            "salt.utils.systemd.has_scope", MagicMock(return_value=False)
+        ), patch.dict(yumpkg.__salt__, salt_mock):
+
+            with patch.dict(yumpkg.__grains__, {"os": "CentOS", "osrelease": 
7}):
+                expected = ["yum", "-y", "remove", full_pkg_string]
+                yumpkg.remove(name)
+                call = cmd_mock.mock_calls[0][1][0]
+                assert call == expected, call
+
+    def test_remove_with_epoch_and_arch_info(self):
+        """
+        Tests that we properly identify a version containing an epoch and arch
+        deinstallation.
+
+        You can deinstall pkgs with or without epoch in combination with the 
arch.
+        Here we test for the absence of the epoch, but the presence for the 
arch:
+
+        .. code-block:: bash
+
+            yum remove PackageKit-yum-1.1.10-2.el7.centos.x86_64
+        """
+        arch = "x86_64"
+        name = "foo"
+        name_and_arch = name + "." + arch
+        installed = "8:3.8.12-4.n.el7"
+        list_pkgs_mock = MagicMock(
+            side_effect=lambda **kwargs: {
+                name_and_arch: [installed]
+                if kwargs.get("versions_as_list", False)
+                else installed
+            }
+        )
+        cmd_mock = MagicMock(
+            return_value={"pid": 12345, "retcode": 0, "stdout": "", "stderr": 
""}
+        )
+        salt_mock = {
+            "cmd.run_all": cmd_mock,
+            "lowpkg.version_cmp": rpm.version_cmp,
+            "pkg_resource.parse_targets": MagicMock(
+                return_value=({name_and_arch: installed}, "repository")
+            ),
+        }
+        full_pkg_string = "-".join((name, installed[2:]))
+        with patch.object(yumpkg, "list_pkgs", list_pkgs_mock), patch(
+            "salt.utils.systemd.has_scope", MagicMock(return_value=False)
+        ), patch.dict(yumpkg.__salt__, salt_mock):
+
+            with patch.dict(yumpkg.__grains__, {"os": "CentOS", "osrelease": 
7}):
+                expected = ["yum", "-y", "remove", full_pkg_string + "." + 
arch]
+                yumpkg.remove(name)
+                call = cmd_mock.mock_calls[0][1][0]
+                assert call == expected, call
+
     def test_install_with_epoch(self):
         """
         Tests that we properly identify a version containing an epoch as an
-- 
2.31.1


++++++ prevent-command-injection-in-the-snapper-module-bsc-.patch ++++++
>From ea02e9398160fad03dd662635ec038b95db2c04a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernan...@suse.com>
Date: Tue, 27 Apr 2021 11:14:20 +0100
Subject: [PATCH] Prevent command injection in the snapper module
 (bsc#1185281) (CVE-2021-31607)

---
 salt/modules/snapper.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py
index 1df3ce9368..6954c3b544 100644
--- a/salt/modules/snapper.py
+++ b/salt/modules/snapper.py
@@ -18,6 +18,7 @@ from __future__ import absolute_import, print_function, 
unicode_literals
 import difflib
 import logging
 import os
+import subprocess
 import time
 
 import salt.utils.files
@@ -561,8 +562,13 @@ def _is_text_file(filename):
     """
     Checks if a file is a text file
     """
-    type_of_file = os.popen("file -bi {0}".format(filename), "r").read()
-    return type_of_file.startswith("text")
+    type_of_file = subprocess.run(
+        ["file", "-bi", filename],
+        check=False,
+        stdout=subprocess.PIPE,
+        universal_newlines=True,
+    ).stdout
+    return type_of_file.startswith('text')
 
 
 def run(function, *args, **kwargs):
-- 
2.31.1


++++++ salt-tmpfiles.d ++++++
--- /var/tmp/diff_new_pack.WWBdJC/_old  2021-09-20 23:33:03.583166649 +0200
+++ /var/tmp/diff_new_pack.WWBdJC/_new  2021-09-20 23:33:03.583166649 +0200
@@ -1,5 +1,5 @@
 # Type Path                    Mode UID  GID     Age Argument
-d      /var/run/salt           0750 root salt
-d      /var/run/salt/master    0750 salt salt
-d      /var/run/salt/minion    0750 root root
+d      /run/salt               0750 root salt
+d      /run/salt/master        0750 salt salt
+d      /run/salt/minion        0750 root root
 

++++++ templates-move-the-globals-up-to-the-environment-jin.patch ++++++
>From 1e8f506827bcf32bfe7e87763fa854a13729f2c8 Mon Sep 17 00:00:00 2001
From: Alberto Planas <apla...@suse.com>
Date: Tue, 31 Aug 2021 11:20:49 +0200
Subject: [PATCH] templates: move the globals up to the Environment
 (Jinja2 3.0.0) (#418)

* jinja: fix TemplateNotFound missing name

The TemplateNotFound exception requires a parameter, name, that is
missing in one of the calls.

File "/usr/lib/python3.8/site-packages/salt/utils/jinja.py", line 158, in 
get_source
    raise TemplateNotFound
TypeError: __init__() missing 1 required positional argument: 'name'

This patch add the missing parameter in the raise call.

Signed-off-by: Alberto Planas <apla...@suse.com>

* templates: move the globals up to the Environment

When creating a Jinja2 environment, we populate the globals in the
Template object that we generate from the environment.  This cause a
problem when there is a {% include "./file.sls" %} in the template, as
cannot find in the environment globals information like the "tpldir",
for example, making the relative path to be unresolved.

Seems that in Jinja2 2.X this behaviour is not present, so attaching the
globals to the Template will make the include to work, but since Jinja2
3.0.0 this is not the case.  Maybe related with the re-architecture from
https://github.com/pallets/jinja/issues/295

This patch populate the globals in the Environment level, making this
and other variables reachable by the Jinja templates.

Fix #55159

Signed-off-by: Alberto Planas <apla...@suse.com>
---
 changelog/55159.fixed          |  1 +
 salt/utils/jinja.py            |  2 +-
 salt/utils/templates.py        |  2 +-
 tests/unit/utils/test_jinja.py | 16 ++++++++++++++++
 4 files changed, 19 insertions(+), 2 deletions(-)
 create mode 100644 changelog/55159.fixed

diff --git a/changelog/55159.fixed b/changelog/55159.fixed
new file mode 100644
index 0000000000..6ee1a78366
--- /dev/null
+++ b/changelog/55159.fixed
@@ -0,0 +1 @@
+Jinja renderer resolves wrong relative paths when importing subdirectories
diff --git a/salt/utils/jinja.py b/salt/utils/jinja.py
index 997d4b1697..e1ac4657f9 100644
--- a/salt/utils/jinja.py
+++ b/salt/utils/jinja.py
@@ -155,7 +155,7 @@ class SaltCacheLoader(BaseLoader):
                     'Relative path "%s" cannot be resolved without an 
environment',
                     template,
                 )
-                raise TemplateNotFound
+                raise TemplateNotFound(template)
             base_path = environment.globals["tpldir"]
             _template = os.path.normpath("/".join((base_path, _template)))
             if _template.split("/", 1)[0] == "..":
diff --git a/salt/utils/templates.py b/salt/utils/templates.py
index 1fda960b2e..f369da5c9e 100644
--- a/salt/utils/templates.py
+++ b/salt/utils/templates.py
@@ -492,9 +492,9 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None):
             )
             decoded_context[key] = salt.utils.data.decode(value)
 
+    jinja_env.globals.update(decoded_context)
     try:
         template = jinja_env.from_string(tmplstr)
-        template.globals.update(decoded_context)
         output = template.render(**decoded_context)
     except jinja2.exceptions.UndefinedError as exc:
         trace = traceback.extract_tb(sys.exc_info()[2])
diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py
index 807e901afa..0219512097 100644
--- a/tests/unit/utils/test_jinja.py
+++ b/tests/unit/utils/test_jinja.py
@@ -612,6 +612,22 @@ class TestGetTemplate(TestCase):
             dict(opts=self.local_opts, saltenv="test", salt=self.local_salt),
         )
 
+    def test_relative_include(self):
+        template = "{% include './hello_import' %}"
+        expected = "Hey world !a b !"
+        filename = os.path.join(self.template_dir, "hello_import")
+        with salt.utils.files.fopen(filename) as fp_:
+            out = render_jinja_tmpl(
+                template,
+                dict(
+                    opts=self.local_opts,
+                    saltenv="test",
+                    salt=self.local_salt,
+                    tpldir=self.template_dir,
+                ),
+            )
+        self.assertEqual(out, expected)
+
 
 class TestJinjaDefaultOptions(TestCase):
     @classmethod
-- 
2.33.0


++++++ transactional_update-detect-recursion-in-the-executo.patch ++++++
>From 1ea573fe35245ab08eb26a757d373ca16c841a1c Mon Sep 17 00:00:00 2001
From: Alberto Planas <apla...@suse.com>
Date: Tue, 27 Apr 2021 14:01:43 +0200
Subject: [PATCH] transactional_update: detect recursion in the
 executor (#359)

---
 salt/executors/transactional_update.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/salt/executors/transactional_update.py 
b/salt/executors/transactional_update.py
index ef7d92bc05..0fa83d730b 100644
--- a/salt/executors/transactional_update.py
+++ b/salt/executors/transactional_update.py
@@ -5,6 +5,8 @@ Transactional executor module
 
 """
 
+import os
+
 import salt.utils.path
 
 # Functions that are mapped into an equivalent one in
@@ -98,6 +100,8 @@ def execute(opts, data, func, args, kwargs):
        add_delegated_functions: [file.copy]
 
     """
+    inside_transaction = os.environ.get("TRANSACTIONAL_UPDATE")
+
     fun = data["fun"]
     module, _ = fun.split(".")
 
@@ -114,11 +118,13 @@ def execute(opts, data, func, args, kwargs):
         delegated_modules |= set(opts.get("add_delegated_modules", []))
         delegated_functions |= set(opts.get("add_delegated_functions", []))
 
-    if fun in DELEGATION_MAP:
+    if fun in DELEGATION_MAP and not inside_transaction:
         result = __executors__["direct_call.execute"](
             opts, data, __salt__[DELEGATION_MAP[fun]], args, kwargs
         )
-    elif module in delegated_modules or fun in delegated_functions:
+    elif (
+        module in delegated_modules or fun in delegated_functions
+    ) and not inside_transaction:
         result = __salt__["transactional_update.call"](fun, *args, **kwargs)
     else:
         result = __executors__["direct_call.execute"](opts, data, func, args, 
kwargs)
-- 
2.31.1


++++++ transactional_update.conf ++++++
# Enable the transactional_update executor
module_executors:
  - transactional_update
  - direct_call
++++++ virt-pass-emulator-when-getting-domain-capabilities-.patch ++++++
>From ddcf5ae80be638ade7634990194c48c5c703d538 Mon Sep 17 00:00:00 2001
From: Cedric Bosdonnat <cedric.bosdon...@free.fr>
Date: Tue, 6 Jul 2021 08:47:25 +0200
Subject: [PATCH] virt: pass emulator when getting domain capabilities
 from libvirt (#394)

On aarch64, for some emulated architectures like armv6l libvirt needs to
have the emulator path to properly return the domain capabilities.

Passing it will avoid virt.all_capabilities to fail on such
architectures.
---
 changelog/60491.fixed           |  1 +
 salt/modules/virt.py            | 10 +++++++---
 tests/unit/modules/test_virt.py |  4 ++++
 3 files changed, 12 insertions(+), 3 deletions(-)
 create mode 100644 changelog/60491.fixed

diff --git a/changelog/60491.fixed b/changelog/60491.fixed
new file mode 100644
index 0000000000..256d29b5fb
--- /dev/null
+++ b/changelog/60491.fixed
@@ -0,0 +1 @@
+Pass emulator path to get guest capabilities from libvirt
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index 2f2aa63957..12b39d76db 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -6770,7 +6770,11 @@ def all_capabilities(**kwargs):
         host_caps = ElementTree.fromstring(conn.getCapabilities())
         domains = [
             [
-                (guest.get("arch", {}).get("name", None), key)
+                (
+                    guest.get("arch", {}).get("name", None),
+                    key,
+                    guest.get("arch", {}).get("emulator", None),
+                )
                 for key in guest.get("arch", {}).get("domains", {}).keys()
             ]
             for guest in [
@@ -6788,10 +6792,10 @@ def all_capabilities(**kwargs):
             "domains": [
                 _parse_domain_caps(
                     ElementTree.fromstring(
-                        conn.getDomainCapabilities(None, arch, None, domain)
+                        conn.getDomainCapabilities(emulator, arch, None, 
domain)
                     )
                 )
-                for (arch, domain) in flattened
+                for (arch, domain, emulator) in flattened
             ],
         }
     finally:
diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py
index 5c7e1e1cc4..c6a76af10f 100644
--- a/tests/unit/modules/test_virt.py
+++ b/tests/unit/modules/test_virt.py
@@ -5057,6 +5057,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
             {"qemu", "kvm"}, {domainCaps["domain"] for domainCaps in 
caps["domains"]},
         )
 
+        self.mock_conn.getDomainCapabilities.assert_called_with(
+            "/usr/bin/qemu-system-x86_64", "x86_64", None, "kvm"
+        )
+
     def test_network_tag(self):
         """
         Test virt._get_net_xml() with VLAN tag
-- 
2.32.0


++++++ virt-use-dev-kvm-to-detect-kvm-383.patch ++++++
>From 73f474fcc7700abff110e3eac653fea5e320ee4f Mon Sep 17 00:00:00 2001
From: Cedric Bosdonnat <cedric.bosdon...@free.fr>
Date: Thu, 24 Jun 2021 11:37:41 +0200
Subject: [PATCH] virt: use /dev/kvm to detect KVM (#383)

checking for kvm_* modules to be loaded is not robust enough since the
kernel could be compiled with builtin modules. /dev/kvm is much more
reliable.
---
 changelog/60419.fixed                        |  1 +
 salt/modules/virt.py                         |  7 +------
 tests/pytests/unit/modules/virt/test_host.py | 19 +++++++++++++++++++
 3 files changed, 21 insertions(+), 6 deletions(-)
 create mode 100644 changelog/60419.fixed

diff --git a/changelog/60419.fixed b/changelog/60419.fixed
new file mode 100644
index 0000000000..44c782da48
--- /dev/null
+++ b/changelog/60419.fixed
@@ -0,0 +1 @@
+Check for /dev/kvm to detect KVM hypervisor.
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index d8a8c51ce5..2f2aa63957 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -5745,12 +5745,7 @@ def _is_kvm_hyper():
     """
     Returns a bool whether or not this node is a KVM hypervisor
     """
-    try:
-        with salt.utils.files.fopen("/proc/modules") as fp_:
-            if "kvm_" not in salt.utils.stringutils.to_unicode(fp_.read()):
-                return False
-    except OSError:
-        # No /proc/modules? Are we on Windows? Or Solaris?
+    if not os.path.exists("/dev/kvm"):
         return False
     return "libvirtd" in __salt__["cmd.run"](__grains__["ps"])
 
diff --git a/tests/pytests/unit/modules/virt/test_host.py 
b/tests/pytests/unit/modules/virt/test_host.py
index 6c9ac79337..c5cadb8aa0 100644
--- a/tests/pytests/unit/modules/virt/test_host.py
+++ b/tests/pytests/unit/modules/virt/test_host.py
@@ -1,5 +1,8 @@
+import os.path
+
 import pytest
 import salt.modules.virt as virt
+from tests.support.mock import MagicMock, patch
 
 from .conftest import loader_modules_config
 
@@ -217,3 +220,19 @@ def test_node_devices(make_mock_device):
             "device name": "pci_0000_02_10_7",
         },
     ]
+
+
+@pytest.mark.parametrize(
+    "dev_kvm, libvirtd", [(True, True), (False, False), (True, False)]
+)
+def test_is_kvm(dev_kvm, libvirtd):
+    """
+    Test the virt._is_kvm_hyper() function
+    """
+    with patch.dict(os.path.__dict__, {"exists": 
MagicMock(return_value=dev_kvm)}):
+        processes = ["libvirtd"] if libvirtd else []
+        with patch.dict(virt.__grains__, {"ps": 
MagicMock(return_value="foo")}):
+            with patch.dict(
+                virt.__salt__, {"cmd.run": MagicMock(return_value=processes)}
+            ):
+                assert virt._is_kvm_hyper() == (dev_kvm and libvirtd)
-- 
2.31.1

Reply via email to