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-04-22 18:03:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/salt (Old) and /work/SRC/openSUSE:Factory/.salt.new.12324 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "salt" Thu Apr 22 18:03:26 2021 rev:117 rq:887060 version:3002.2 Changes: -------- --- /work/SRC/openSUSE:Factory/salt/salt.changes 2021-03-12 13:30:24.906081393 +0100 +++ /work/SRC/openSUSE:Factory/.salt.new.12324/salt.changes 2021-04-22 18:03:37.474475050 +0200 @@ -1,0 +2,52 @@ +Tue Apr 20 12:18:06 UTC 2021 - Pablo Su??rez Hern??ndez <[email protected]> + +- Improvements on "ansiblegate" module: + * New methods: ansible.targets / ansible.discover_playbooks + * General bugfixes + +- Added: + * improvements-on-ansiblegate-module-354.patch + +------------------------------------------------------------------- +Tue Apr 13 15:03:48 UTC 2021 - Pablo Su??rez Hern??ndez <[email protected]> + +- Regression fix of salt-ssh on processing some targets + +- Added: + * regression-fix-of-salt-ssh-on-processing-targets-353.patch + +------------------------------------------------------------------- +Tue Apr 13 08:40:32 UTC 2021 - Pablo Su??rez Hern??ndez <[email protected]> + +- Add support for Alibaba Cloud Linux 2 (Aliyun Linux) + +- Added: + * add-alibaba-cloud-linux-2-by-backporting-upstream-s-.patch + +------------------------------------------------------------------- +Fri Apr 9 14:39:50 UTC 2021 - Victor Zhestkov <[email protected]> + +- Update target fix for salt-ssh to process targets list (bsc#1179831) + +- Added: + * update-target-fix-for-salt-ssh-to-process-targets-li.patch + +------------------------------------------------------------------- +Fri Apr 9 10:33:54 UTC 2021 - Alexander Graul <[email protected]> + +- Add notify beacon for Debian/Ubuntu systems +- Add core grains support for AlmaLinux and Alibaba Could Linux + +- Added: + * add-almalinux-and-alibaba-cloud-linux-to-the-os-fami.patch + * notify-beacon-for-debian-ubuntu-systems-347.patch + +------------------------------------------------------------------- +Wed Mar 17 14:17:05 UTC 2021 - Jochen Breuer <[email protected]> + +- Allow vendor change option with zypper + +- Added: + * allow-vendor-change-option-with-zypper-313.patch + +------------------------------------------------------------------- New: ---- add-alibaba-cloud-linux-2-by-backporting-upstream-s-.patch add-almalinux-and-alibaba-cloud-linux-to-the-os-fami.patch allow-vendor-change-option-with-zypper-313.patch improvements-on-ansiblegate-module-354.patch notify-beacon-for-debian-ubuntu-systems-347.patch regression-fix-of-salt-ssh-on-processing-targets-353.patch update-target-fix-for-salt-ssh-to-process-targets-li.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ salt.spec ++++++ --- /var/tmp/diff_new_pack.YiWDsB/_old 2021-04-22 18:03:39.486478161 +0200 +++ /var/tmp/diff_new_pack.YiWDsB/_new 2021-04-22 18:03:39.490478167 +0200 @@ -381,6 +381,21 @@ Patch160: 3002-set-distro-requirement-to-oldest-supported-vers.patch # PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59693 Patch161: virt.network_update-handle-missing-ipv4-netmask-attr.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/313 +Patch162: allow-vendor-change-option-with-zypper-313.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59404 +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/341 +Patch163: add-almalinux-and-alibaba-cloud-linux-to-the-os-fami.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/347 +Patch164: notify-beacon-for-debian-ubuntu-systems-347.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/336 +Patch165: update-target-fix-for-salt-ssh-to-process-targets-li.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59687 +Patch166: add-alibaba-cloud-linux-2-by-backporting-upstream-s-.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/353 +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 BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: logrotate @@ -937,6 +952,13 @@ %patch159 -p1 %patch160 -p1 %patch161 -p1 +%patch162 -p1 +%patch163 -p1 +%patch164 -p1 +%patch165 -p1 +%patch166 -p1 +%patch167 -p1 +%patch168 -p1 %build # Putting /usr/bin at the front of $PATH is needed for RHEL/RES 7. Without this ++++++ _lastrevision ++++++ --- /var/tmp/diff_new_pack.YiWDsB/_old 2021-04-22 18:03:39.558478272 +0200 +++ /var/tmp/diff_new_pack.YiWDsB/_new 2021-04-22 18:03:39.558478272 +0200 @@ -1 +1 @@ -68bd2c45e40cf64ac426a7e5833daa076ab10cfe \ No newline at end of file +a94708ad2eba9aa15413d989ab3361b2c980589e \ No newline at end of file ++++++ add-alibaba-cloud-linux-2-by-backporting-upstream-s-.patch ++++++ >From ec0d11ed66e8541a9ccaebc85aab4724013fb71f Mon Sep 17 00:00:00 2001 From: Pau Garcia Quiles <[email protected]> Date: Tue, 13 Apr 2021 10:31:09 +0200 Subject: [PATCH] Add Alibaba Cloud Linux 2 by backporting upstream's grain and discarding my own (#352) --- salt/grains/core.py | 4 ++-- tests/unit/grains/test_core.py | 26 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/salt/grains/core.py b/salt/grains/core.py index 09f9d29788..2b965a2a8a 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -1547,7 +1547,7 @@ _OS_NAME_MAP = { "slesexpand": "RES", "linuxmint": "Mint", "neon": "KDE neon", - "alibaba": "Alibaba Cloud (Aliyun)", + "alibabaclo": "Alinux", } # Map the 'os' grain to the 'os_family' grain @@ -1622,7 +1622,7 @@ _OS_FAMILY_MAP = { "AIX": "AIX", "TurnKey": "Debian", "AstraLinuxCE": "Debian", - "Alibaba Cloud (Aliyun)": "RedHat", + "Alinux": "RedHat", } # Matches any possible format: diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py index 6aa05abe40..8280d6de47 100644 --- a/tests/unit/grains/test_core.py +++ b/tests/unit/grains/test_core.py @@ -782,17 +782,25 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): Test if OS grains are parsed correctly in Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS ''' _os_release_map = { - '_linux_distribution': ('Alibaba Cloud Linux (Aliyun Linux)', '2.1903', 'Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS (Hunting Beagle)'), + "os_release_file": { + "NAME": "Alibaba Cloud Linux (Aliyun Linux)", + "VERSION": "2.1903 LTS (Hunting Beagle)", + "VERSION_ID": "2.1903", + "PRETTY_NAME": "Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS (Hunting Beagle)", + "ID": "alinux", + "ANSI_COLOR": "0;31", + }, + "_linux_distribution": ("alinux", "2.1903", "LTS"), } expectation = { - 'os': 'Alibaba Cloud (Aliyun)', - 'os_family': 'RedHat', - 'oscodename': 'Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS (Hunting Beagle)', - 'osfullname': 'Alibaba Cloud Linux (Aliyun Linux)', - 'osrelease': '2.1903', - 'osrelease_info': (2, 1903), - 'osmajorrelease': 2, - 'osfinger': 'Alibaba Cloud Linux (Aliyun Linux)-2', + "os": "Alinux", + "os_family": "RedHat", + "oscodename": "Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS (Hunting Beagle)", + "osfullname": "Alibaba Cloud Linux (Aliyun Linux)", + "osrelease": "2.1903", + "osrelease_info": (2, 1903), + "osmajorrelease": 2, + "osfinger": "Alibaba Cloud Linux (Aliyun Linux)-2", } self._run_os_grains_tests(None, _os_release_map, expectation) -- 2.30.2 ++++++ add-almalinux-and-alibaba-cloud-linux-to-the-os-fami.patch ++++++ >From beec6f3945bda722bfe9c0aa606065f04c89bc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Gonz=C3=A1lez=20Gil?= <[email protected]> Date: Wed, 24 Mar 2021 14:12:34 +0100 Subject: [PATCH] Add AlmaLinux and Alibaba Cloud Linux to the OS Family list (#341) * Add AlmaLinux and Alibaba Cloud Linux to the OS Family list * Fix some grains tests --- salt/grains/core.py | 4 +++ tests/unit/grains/test_core.py | 51 +++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/salt/grains/core.py b/salt/grains/core.py index 5634327623..09f9d29788 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -1532,6 +1532,7 @@ _OS_NAME_MAP = { "oracleserv": "OEL", "cloudserve": "CloudLinux", "cloudlinux": "CloudLinux", + "almalinux": "AlmaLinux", "pidora": "Fedora", "scientific": "ScientificLinux", "synology": "Synology", @@ -1546,6 +1547,7 @@ _OS_NAME_MAP = { "slesexpand": "RES", "linuxmint": "Mint", "neon": "KDE neon", + "alibaba": "Alibaba Cloud (Aliyun)", } # Map the 'os' grain to the 'os_family' grain @@ -1563,6 +1565,7 @@ _OS_FAMILY_MAP = { "Scientific": "RedHat", "Amazon": "RedHat", "CloudLinux": "RedHat", + "AlmaLinux": "RedHat", "OVS": "RedHat", "OEL": "RedHat", "XCP": "RedHat", @@ -1619,6 +1622,7 @@ _OS_FAMILY_MAP = { "AIX": "AIX", "TurnKey": "Debian", "AstraLinuxCE": "Debian", + "Alibaba Cloud (Aliyun)": "RedHat", } # Matches any possible format: diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py index 15de4e363e..6aa05abe40 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_almalinux_8_os_grains(self): + """ + Test if OS grains are parsed correctly in AlmaLinux 8 + """ + _os_release_map = { + "os_release_file": { + "NAME": "AlmaLinux", + "VERSION_ID": "8.3", + "PRETTY_NAME": "AlmaLinux 8", + "ID": "almalinux", + "ANSI_COLOR": "0;31", + "CPE_NAME": "cpe:/o:almalinux:almalinux:8.3", + }, + "_linux_distribution": ("almaLinux", "8.3", ""), + } + + expectation = { + "os": "AlmaLinux", + "os_family": "RedHat", + "oscodename": "AlmaLinux 8", + "osfullname": "AlmaLinux", + "osrelease": "8.3", + "osrelease_info": (8, 3,), + "osmajorrelease": 8, + "osfinger": "AlmaLinux-8", + } + self._run_os_grains_tests(None, _os_release_map, expectation) + def test_unicode_error(self): raise_unicode_mock = MagicMock( name="raise_unicode_error", side_effect=UnicodeError @@ -733,7 +762,7 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): Test if OS grains are parsed correctly in Astra Linux CE 2.12.22 "orel" """ _os_release_map = { - "linux_distribution": ("AstraLinuxCE", "2.12.22", "orel"), + "_linux_distribution": ("AstraLinuxCE", "2.12.22", "orel"), } expectation = { "os": "AstraLinuxCE", @@ -747,6 +776,26 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): } self._run_os_grains_tests("astralinuxce-2.12.22", _os_release_map, expectation) + @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + def test_aliyunlinux2_os_grains(self): + ''' + Test if OS grains are parsed correctly in Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS + ''' + _os_release_map = { + '_linux_distribution': ('Alibaba Cloud Linux (Aliyun Linux)', '2.1903', 'Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS (Hunting Beagle)'), + } + expectation = { + 'os': 'Alibaba Cloud (Aliyun)', + 'os_family': 'RedHat', + 'oscodename': 'Alibaba Cloud Linux (Aliyun Linux) 2.1903 LTS (Hunting Beagle)', + 'osfullname': 'Alibaba Cloud Linux (Aliyun Linux)', + 'osrelease': '2.1903', + 'osrelease_info': (2, 1903), + 'osmajorrelease': 2, + 'osfinger': 'Alibaba Cloud Linux (Aliyun Linux)-2', + } + self._run_os_grains_tests(None, _os_release_map, expectation) + @skipIf(not salt.utils.platform.is_windows(), "System is not Windows") def test_windows_platform_data(self): """ -- 2.30.2 ++++++ allow-vendor-change-option-with-zypper-313.patch ++++++ >From 33ad6876a04e800afc08748133dc568a5e362903 Mon Sep 17 00:00:00 2001 From: Martin Seidl <[email protected]> Date: Wed, 17 Mar 2021 14:05:42 +0100 Subject: [PATCH] Allow vendor change option with zypper (#313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add patch support for allow vendor change option with zypper * adjust unit tests vendor change refactor, dropping cli arg * Fix pr issues Co-authored-by: Pablo Su??rez Hern??ndez <[email protected]> * Fix unit test for allow vendor change on upgrade * Add unit test with unsupported zypper version Co-authored-by: Pablo Su??rez Hern??ndez <[email protected]> --- salt/modules/zypperpkg.py | 58 +++++++++++++--- tests/unit/modules/test_zypperpkg.py | 99 ++++++++++++++++++---------- 2 files changed, 112 insertions(+), 45 deletions(-) diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py index 6f22994bf0..b35792237c 100644 --- a/salt/modules/zypperpkg.py +++ b/salt/modules/zypperpkg.py @@ -105,6 +105,10 @@ class _Zypper: ZYPPER_LOCK = "/var/run/zypp.pid" TAG_RELEASED = "zypper/released" TAG_BLOCKED = "zypper/blocked" + # Dist upgrade vendor change support (SLE12+) + dup_avc = False + # Install/Patch/Upgrade vendor change support (SLE15+) + inst_avc = False def __init__(self): """ @@ -218,6 +222,21 @@ class _Zypper: def pid(self): return self.__call_result.get("pid", "") + def refresh_zypper_flags(self): + try: + zypp_version = version('zypper') + # zypper version 1.11.34 in SLE12 update supports vendor change for only dist upgrade + if version_cmp(zypp_version, '1.11.34') >= 0: + # zypper version supports vendor change for dist upgrade + self.dup_avc = True + # zypper version 1.14.8 in SLE15 update supports vendor change in install/patch/upgrading + if version_cmp(zypp_version, '1.14.8') >= 0: + self.inst_avc = True + else: + log.error("Failed to compare Zypper version") + except Exception as ex: + log.error("Unable to get Zypper version: {}".format(ex)) + def _is_error(self): """ Is this is an error code? @@ -1431,6 +1450,7 @@ def install( no_recommends=False, root=None, inclusion_detection=False, + novendorchange=True, **kwargs ): """ @@ -1478,6 +1498,9 @@ def install( skip_verify Skip the GPG verification check (e.g., ``--no-gpg-checks``) + novendorchange + Disallow vendor change + version Can be either a version number, or the combination of a comparison operator (<, >, <=, >=, =) and a version number (ex. '>1.2.3-4'). @@ -1638,6 +1661,15 @@ def install( cmd_install.append( kwargs.get("resolve_capabilities") and "--capability" or "--name" ) + # Install / patching / upgrade with vendor change support is only in SLE 15+ opensuse Leap 15+ + if not novendorchange: + __zypper__(root=root).refresh_zypper_flags() + if __zypper__(root=root).inst_avc: + cmd_install.append("--allow-vendor-change") + log.info("Enabling vendor changes") + else: + log.warning("Enabling/Disabling vendor changes is not supported on this Zypper version") + if not refresh: cmd_install.insert(0, "--no-refresh") @@ -1793,19 +1825,25 @@ def upgrade( cmd_update.extend(["--from" if dist_upgrade else "--repo", repo]) log.info("Targeting repos: %s", fromrepo) - if dist_upgrade: - # TODO: Grains validation should be moved to Zypper class - if __grains__["osrelease_info"][0] > 11: - if novendorchange: - cmd_update.append("--no-allow-vendor-change") - log.info("Disabling vendor changes") - else: + if not novendorchange: + __zypper__(root=root).refresh_zypper_flags() + if dist_upgrade: + if __zypper__(root=root).dup_avc: cmd_update.append("--allow-vendor-change") log.info("Enabling vendor changes") + else: + log.warning( + "Enabling/Disabling vendor changes is not supported on this Zypper version" + ) else: - log.warning( - "Enabling/Disabling vendor changes is not supported on this Zypper version" - ) + # Install / patching / upgrade with vendor change support is only in SLE 15+ opensuse Leap 15+ + if __zypper__(root=root).inst_avc: + cmd_update.append("--allow-vendor-change") + log.info("Enabling vendor changes") + else: + log.warning( + "Enabling/Disabling vendor changes is not supported on this Zypper version" + ) if no_recommends: cmd_update.append("--no-recommends") diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py index 9c4a224c55..f32c382d7f 100644 --- a/tests/unit/modules/test_zypperpkg.py +++ b/tests/unit/modules/test_zypperpkg.py @@ -644,7 +644,6 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): zypper_mock.assert_any_call( "dist-upgrade", "--auto-agree-with-licenses", - "--no-allow-vendor-change", ) with patch( @@ -691,46 +690,80 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): "dist-upgrade", "--auto-agree-with-licenses", "--dry-run", - "--no-allow-vendor-change", ) zypper_mock.assert_any_call( "dist-upgrade", "--auto-agree-with-licenses", "--dry-run", - "--no-allow-vendor-change", ) with patch( "salt.modules.zypperpkg.list_pkgs", - MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]) ): - ret = zypper.upgrade( - dist_upgrade=True, - dryrun=True, - fromrepo=["Dummy", "Dummy2"], - novendorchange=False, - ) - zypper_mock.assert_any_call( - "dist-upgrade", - "--auto-agree-with-licenses", - "--dry-run", - "--from", - "Dummy", - "--from", - "Dummy2", - "--allow-vendor-change", - ) - zypper_mock.assert_any_call( - "dist-upgrade", - "--auto-agree-with-licenses", - "--dry-run", - "--from", - "Dummy", - "--from", - "Dummy2", - "--allow-vendor-change", - "--debug-solver", - ) + with patch.dict(zypper.__salt__, + {'pkg_resource.version': MagicMock(return_value='1.15'), + 'lowpkg.version_cmp': MagicMock(return_value=1)}): + ret = zypper.upgrade( + dist_upgrade=True, + dryrun=True, + fromrepo=["Dummy", "Dummy2"], + novendorchange=False, + ) + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--dry-run", + "--from", + "Dummy", + "--from", + "Dummy2", + "--allow-vendor-change", + ) + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--dry-run", + "--from", + "Dummy", + "--from", + "Dummy2", + "--allow-vendor-change", + "--debug-solver", + ) + + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]) + ): + with patch.dict(zypper.__salt__, + {'pkg_resource.version': MagicMock(return_value='1.11'), + 'lowpkg.version_cmp': MagicMock(return_value=1)}): + ret = zypper.upgrade( + dist_upgrade=True, + dryrun=True, + fromrepo=["Dummy", "Dummy2"], + novendorchange=False, + ) + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--dry-run", + "--from", + "Dummy", + "--from", + "Dummy2", + ) + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--dry-run", + "--from", + "Dummy", + "--from", + "Dummy2", + "--debug-solver", + ) with patch( "salt.modules.zypperpkg.list_pkgs", @@ -750,7 +783,6 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): "Dummy", "--from", "Dummy2", - "--no-allow-vendor-change", ) zypper_mock.assert_any_call( "dist-upgrade", @@ -760,7 +792,6 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): "Dummy", "--from", "Dummy2", - "--no-allow-vendor-change", "--debug-solver", ) @@ -797,7 +828,6 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): "Dummy", "--from", "Dummy2", - "--no-allow-vendor-change", ) with patch( @@ -911,7 +941,6 @@ Repository 'DUMMY' not found by its alias, number, or URI. "--auto-agree-with-licenses", "--from", "DUMMY", - "--no-allow-vendor-change", ) def test_upgrade_available(self): -- 2.30.1 ++++++ improvements-on-ansiblegate-module-354.patch ++++++ >From aa0f845e2bbc37332db04c583f475cfe25304db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <[email protected]> Date: Tue, 20 Apr 2021 11:01:26 +0100 Subject: [PATCH] Improvements on "ansiblegate" module (#354) * Allow collecting Ansible Inventory from a minion * Prevent crashing if ansible-playbook doesn't return JSON * Add new 'ansible.discover_playbooks' method * Include custom inventory when discovering Ansible playbooks * Enhance 'ansible.discover_playbooks' to accept a list of locations * Remove unused constants from Ansible utils * Avoid string concatenation to calculate extra cmd args * Add unit test for ansible.targets * Improve Ansible roster targetting * Add tests for new ansiblegate module functions * Fix issue dealing with ungrouped targets on inventory * Enable ansible utils for ansible roster tests * Remove unnecessary code from Ansible utils * Fix pylint issue * Fix issue in documentation --- salt/modules/ansiblegate.py | 166 +++++++++++++++++- salt/roster/ansible.py | 18 +- salt/utils/ansible.py | 44 +++++ .../pytests/unit/modules/test_ansiblegate.py | 94 +++++++++- .../example_playbooks/example-playbook2/hosts | 7 + .../example-playbook2/site.yml | 28 +++ .../playbooks/example_playbooks/playbook1.yml | 5 + tests/unit/roster/test_ansible.py | 2 +- 8 files changed, 354 insertions(+), 10 deletions(-) create mode 100644 salt/utils/ansible.py create mode 100644 tests/unit/files/playbooks/example_playbooks/example-playbook2/hosts create mode 100644 tests/unit/files/playbooks/example_playbooks/example-playbook2/site.yml create mode 100644 tests/unit/files/playbooks/example_playbooks/playbook1.yml diff --git a/salt/modules/ansiblegate.py b/salt/modules/ansiblegate.py index 5d4b986ec2..4f96607a07 100644 --- a/salt/modules/ansiblegate.py +++ b/salt/modules/ansiblegate.py @@ -426,7 +426,171 @@ def playbooks( } ret = __salt__["cmd.run_all"](**cmd_kwargs) log.debug("Ansible Playbook Return: %s", ret) - retdata = json.loads(ret["stdout"]) + try: + retdata = json.loads(ret["stdout"]) + except ValueError: + retdata = ret if "retcode" in ret: __context__["retcode"] = retdata["retcode"] = ret["retcode"] return retdata + + +def targets(**kwargs): + """ + Return the inventory from an Ansible inventory_file + + :param inventory: + The inventory file to read the inventory from. Default: "/etc/ansible/hosts" + + :param yaml: + Return the inventory as yaml output. Default: False + + :param export: + Return inventory as export format. Default: False + + CLI Example: + + .. code-block:: bash + + salt 'ansiblehost' ansible.targets + salt 'ansiblehost' ansible.targets inventory=my_custom_inventory + + """ + return __utils__["ansible.targets"](**kwargs) + + +def discover_playbooks(path=None, + locations=None, + playbook_extension=None, + hosts_filename=None, + syntax_check=False): + """ + Discover Ansible playbooks stored under the given path or from multiple paths (locations) + + This will search for files matching with the playbook file extension under the given + root path and will also look for files inside the first level of directories in this path. + + The return of this function would be a dict like this: + + .. code-block:: python + + { + "/home/foobar/": { + "my_ansible_playbook.yml": { + "fullpath": "/home/foobar/playbooks/my_ansible_playbook.yml", + "custom_inventory": "/home/foobar/playbooks/hosts" + }, + "another_playbook.yml": { + "fullpath": "/home/foobar/playbooks/another_playbook.yml", + "custom_inventory": "/home/foobar/playbooks/hosts" + }, + "lamp_simple/site.yml": { + "fullpath": "/home/foobar/playbooks/lamp_simple/site.yml", + "custom_inventory": "/home/foobar/playbooks/lamp_simple/hosts" + }, + "lamp_proxy/site.yml": { + "fullpath": "/home/foobar/playbooks/lamp_proxy/site.yml", + "custom_inventory": "/home/foobar/playbooks/lamp_proxy/hosts" + } + }, + "/srv/playbooks/": { + "example_playbook/example.yml": { + "fullpath": "/srv/playbooks/example_playbook/example.yml", + "custom_inventory": "/srv/playbooks/example_playbook/hosts" + } + } + } + + :param path: + Path to discover playbooks from. + + :param locations: + List of paths to discover playbooks from. + + :param playbook_extension: + File extension of playbooks file to search for. Default: "yml" + + :param hosts_filename: + Filename of custom playbook inventory to search for. Default: "hosts" + + :param syntax_check: + Skip playbooks that do not pass "ansible-playbook --syntax-check" validation. Default: False + + :return: + The discovered playbooks under the given paths + + CLI Example: + + .. code-block:: bash + + salt 'ansiblehost' ansible.discover_playbooks path=/srv/playbooks/ + salt 'ansiblehost' ansible.discover_playbooks locations='["/srv/playbooks/", "/srv/foobar"]' + + """ + + if not path and not locations: + raise CommandExecutionError("You have to specify either 'path' or 'locations' arguments") + + if path and locations: + raise CommandExecutionError("You cannot specify 'path' and 'locations' at the same time") + + if not playbook_extension: + playbook_extension = "yml" + if not hosts_filename: + hosts_filename = "hosts" + + if path: + if not os.path.isabs(path): + raise CommandExecutionError("The given path is not an absolute path: {}".format(path)) + if not os.path.isdir(path): + raise CommandExecutionError("The given path is not a directory: {}".format(path)) + return {path: _explore_path(path, playbook_extension, hosts_filename, syntax_check)} + + if locations: + all_ret = {} + for location in locations: + all_ret[location] = _explore_path(location, playbook_extension, hosts_filename, syntax_check) + return all_ret + + +def _explore_path(path, playbook_extension, hosts_filename, syntax_check): + ret = {} + + if not os.path.isabs(path): + log.error("The given path is not an absolute path: {}".format(path)) + return ret + if not os.path.isdir(path): + log.error("The given path is not a directory: {}".format(path)) + return ret + + try: + # Check files in the given path + for _f in os.listdir(path): + _path = os.path.join(path, _f) + if os.path.isfile(_path) and _path.endswith("." + playbook_extension): + ret[_f] = {"fullpath": _path} + # Check for custom inventory file + if os.path.isfile(os.path.join(path, hosts_filename)): + ret[_f].update({"custom_inventory": os.path.join(path, hosts_filename)}) + elif os.path.isdir(_path): + # Check files in the 1st level of subdirectories + for _f2 in os.listdir(_path): + _path2 = os.path.join(_path, _f2) + if os.path.isfile(_path2) and _path2.endswith("." + playbook_extension): + ret[os.path.join(_f, _f2)] = {"fullpath": _path2} + # Check for custom inventory file + if os.path.isfile(os.path.join(_path, hosts_filename)): + ret[os.path.join(_f, _f2)].update({"custom_inventory": os.path.join(_path, hosts_filename)}) + except Exception as exc: + raise CommandExecutionError("There was an exception while discovering playbooks: {}".format(exc)) + + # Run syntax check validation + if syntax_check: + check_command = ["ansible-playbook", "--syntax-check"] + try: + for pb in list(ret): + if __salt__["cmd.retcode"](check_command + [ret[pb]]): + del ret[pb] + except Exception as exc: + raise CommandExecutionError("There was an exception while checking syntax of playbooks: {}".format(exc)) + return ret diff --git a/salt/roster/ansible.py b/salt/roster/ansible.py index f17316bdd7..cc61f6fb7d 100644 --- a/salt/roster/ansible.py +++ b/salt/roster/ansible.py @@ -89,7 +89,6 @@ Any of the [groups] or direct hostnames will return. The 'all' is special, and """ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals - import copy import fnmatch @@ -121,27 +120,32 @@ def targets(tgt, tgt_type="glob", **kwargs): Return the targets from the ansible inventory_file Default: /etc/salt/roster """ - inventory = __runner__["salt.cmd"]( - "cmd.run", "ansible-inventory -i {0} --list".format(get_roster_file(__opts__)) - ) - __context__["inventory"] = __utils__["json.loads"]( - __utils__["stringutils.to_str"](inventory) + __context__["inventory"] = __utils__["ansible.targets"]( + inventory=get_roster_file(__opts__), **kwargs ) if tgt_type == "glob": hosts = [ host for host in _get_hosts_from_group("all") if fnmatch.fnmatch(host, tgt) ] + elif tgt_type == "list": + hosts = [host for host in _get_hosts_from_group("all") if host in tgt] elif tgt_type == "nodegroup": hosts = _get_hosts_from_group(tgt) + else: + hosts = [] + return {host: _get_hostvars(host) for host in hosts} def _get_hosts_from_group(group): inventory = __context__["inventory"] + if group not in inventory: + return [] hosts = [host for host in inventory[group].get("hosts", [])] for child in inventory[group].get("children", []): - if child != "ungrouped": + child_info = _get_hosts_from_group(child) + if child_info not in hosts: hosts.extend(_get_hosts_from_group(child)) return hosts diff --git a/salt/utils/ansible.py b/salt/utils/ansible.py new file mode 100644 index 0000000000..ee85cb656c --- /dev/null +++ b/salt/utils/ansible.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals +import logging +import os + +# Import Salt libs +import salt.utils.json +import salt.utils.path +import salt.utils.stringutils +import salt.modules.cmdmod +from salt.exceptions import CommandExecutionError + +__virtualname__ = "ansible" + +log = logging.getLogger(__name__) + + +def __virtual__(): # pylint: disable=expected-2-blank-lines-found-0 + if salt.utils.path.which("ansible-inventory"): + return __virtualname__ + return (False, "Install `ansible` to use inventory") + + +def targets(inventory="/etc/ansible/hosts", **kwargs): + """ + Return the targets from the ansible inventory_file + Default: /etc/salt/roster + """ + if not os.path.isfile(inventory): + raise CommandExecutionError("Inventory file not found: {}".format(inventory)) + + extra_cmd = [] + if "export" in kwargs: + extra_cmd.append("--export") + if "yaml" in kwargs: + extra_cmd.append("--yaml") + inv = salt.modules.cmdmod.run( + "ansible-inventory -i {} --list {}".format(inventory, " ".join(extra_cmd)) + ) + if kwargs.get("yaml", False): + return salt.utils.stringutils.to_str(inv) + else: + return salt.utils.json.loads(salt.utils.stringutils.to_str(inv)) diff --git a/tests/pytests/unit/modules/test_ansiblegate.py b/tests/pytests/unit/modules/test_ansiblegate.py index 42c0968a6e..24c7e5e6b3 100644 --- a/tests/pytests/unit/modules/test_ansiblegate.py +++ b/tests/pytests/unit/modules/test_ansiblegate.py @@ -8,8 +8,11 @@ import pytest import salt.modules.ansiblegate as ansible import salt.utils.path import salt.utils.platform +import salt.config +import salt.loader from salt.exceptions import LoaderError from tests.support.mock import MagicMock, MockTimedProc, patch +from tests.support.runtests import RUNTIME_VARS pytestmark = pytest.mark.skipif( salt.utils.platform.is_windows(), reason="Not supported on Windows" @@ -18,7 +21,7 @@ pytestmark = pytest.mark.skipif( @pytest.fixture def configure_loader_modules(): - return {ansible: {}} + return {ansible: {"__utils__": {}}} @pytest.fixture @@ -201,3 +204,92 @@ def test_ansible_playbooks_return_retcode(resolver): ): ret = ansible.playbooks("fake-playbook.yml") assert "retcode" in ret + + +def test_ansible_targets(): + """ + Test ansible.targets execution module function. + :return: + """ + ansible_inventory_ret = """ +{ + "_meta": { + "hostvars": { + "uyuni-stable-ansible-centos7-1.tf.local": { + "ansible_ssh_private_key_file": "/etc/ansible/my_ansible_private_key" + }, + "uyuni-stable-ansible-centos7-2.tf.local": { + "ansible_ssh_private_key_file": "/etc/ansible/my_ansible_private_key" + } + } + }, + "all": { + "children": [ + "ungrouped" + ] + }, + "ungrouped": { + "hosts": [ + "uyuni-stable-ansible-centos7-1.tf.local", + "uyuni-stable-ansible-centos7-2.tf.local" + ] + } +} + """ + ansible_inventory_mock = MagicMock(return_value=ansible_inventory_ret) + with patch("salt.utils.path.which", MagicMock(return_value=True)): + opts = salt.config.DEFAULT_MINION_OPTS.copy() + utils = salt.loader.utils(opts, whitelist=["ansible"]) + with patch("salt.modules.cmdmod.run", ansible_inventory_mock), patch.dict( + ansible.__utils__, utils), patch( + "os.path.isfile", MagicMock(return_value=True) + ): + ret = ansible.targets() + assert ansible_inventory_mock.call_args + assert "_meta" in ret + assert "uyuni-stable-ansible-centos7-1.tf.local" in ret["_meta"]["hostvars"] + assert "ansible_ssh_private_key_file" in ret["_meta"]["hostvars"]["uyuni-stable-ansible-centos7-1.tf.local"] + assert "all" in ret + assert len(ret["ungrouped"]["hosts"]) == 2 + + +def test_ansible_discover_playbooks_single_path(): + playbooks_dir = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/playbooks/example_playbooks/" + ) + ret = ansible.discover_playbooks(playbooks_dir) + assert playbooks_dir in ret + assert ret[playbooks_dir]["playbook1.yml"] == { + "fullpath": os.path.join(playbooks_dir, "playbook1.yml") + } + assert ret[playbooks_dir]["example-playbook2/site.yml"] == { + "fullpath": os.path.join(playbooks_dir, "example-playbook2/site.yml"), + "custom_inventory": os.path.join(playbooks_dir, "example-playbook2/hosts"), + } + + +def test_ansible_discover_playbooks_single_path_using_parameters(): + playbooks_dir = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/playbooks/example_playbooks/" + ) + ret = ansible.discover_playbooks( + playbooks_dir, playbook_extension="foobar", hosts_filename="deadbeaf" + ) + assert playbooks_dir in ret + assert ret[playbooks_dir] == {} + + +def test_ansible_discover_playbooks_multiple_locations(): + playbooks_dir = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/playbooks/example_playbooks/" + ) + ret = ansible.discover_playbooks(locations=[playbooks_dir, "/tmp/foobar"]) + assert playbooks_dir in ret + assert "/tmp/foobar" in ret + assert ret[playbooks_dir]["playbook1.yml"] == { + "fullpath": os.path.join(playbooks_dir, "playbook1.yml") + } + assert ret[playbooks_dir]["example-playbook2/site.yml"] == { + "fullpath": os.path.join(playbooks_dir, "example-playbook2/site.yml"), + "custom_inventory": os.path.join(playbooks_dir, "example-playbook2/hosts"), + } diff --git a/tests/unit/files/playbooks/example_playbooks/example-playbook2/hosts b/tests/unit/files/playbooks/example_playbooks/example-playbook2/hosts new file mode 100644 index 0000000000..75783285f6 --- /dev/null +++ b/tests/unit/files/playbooks/example_playbooks/example-playbook2/hosts @@ -0,0 +1,7 @@ +[databases] +host1 +host2 + +[webservers] +host3 +host4 diff --git a/tests/unit/files/playbooks/example_playbooks/example-playbook2/site.yml b/tests/unit/files/playbooks/example_playbooks/example-playbook2/site.yml new file mode 100644 index 0000000000..a64ebd5e18 --- /dev/null +++ b/tests/unit/files/playbooks/example_playbooks/example-playbook2/site.yml @@ -0,0 +1,28 @@ +--- +- name: update web servers + hosts: webservers + remote_user: root + + tasks: + - name: ensure apache is at the latest version + yum: + name: httpd + state: latest + - name: write the apache config file + template: + src: /srv/httpd.j2 + dest: /etc/httpd.conf + +- name: update db servers + hosts: databases + remote_user: root + + tasks: + - name: ensure postgresql is at the latest version + yum: + name: postgresql + state: latest + - name: ensure that postgresql is started + service: + name: postgresql + state: started diff --git a/tests/unit/files/playbooks/example_playbooks/playbook1.yml b/tests/unit/files/playbooks/example_playbooks/playbook1.yml new file mode 100644 index 0000000000..e258a101e1 --- /dev/null +++ b/tests/unit/files/playbooks/example_playbooks/playbook1.yml @@ -0,0 +1,5 @@ +--- +- hosts: all + gather_facts: false + tasks: + - ping: diff --git a/tests/unit/roster/test_ansible.py b/tests/unit/roster/test_ansible.py index a5cdcbbdbc..8bc9c1c6f7 100644 --- a/tests/unit/roster/test_ansible.py +++ b/tests/unit/roster/test_ansible.py @@ -71,7 +71,7 @@ class AnsibleRosterTestCase(TestCase, mixins.LoaderModuleMockMixin): opts = salt.config.master_config( os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") ) - utils = salt.loader.utils(opts, whitelist=["json", "stringutils"]) + utils = salt.loader.utils(opts, whitelist=["json", "stringutils", "ansible"]) runner = salt.loader.runner(opts, utils=utils, whitelist=["salt"]) return {ansible: {"__utils__": utils, "__opts__": {}, "__runner__": runner}} -- 2.31.1 ++++++ notify-beacon-for-debian-ubuntu-systems-347.patch ++++++ >From e0f8087409bdff4c3036e38ed4f22f5f031306e8 Mon Sep 17 00:00:00 2001 From: Ricardo Mateus <[email protected]> Date: Fri, 9 Apr 2021 10:57:27 +0100 Subject: [PATCH] Notify beacon for Debian/Ubuntu systems (#347) Signed-off-by: Ricardo Mateus <[email protected]> (cherry picked from commit 33d6baebba94cc7a66d5555de984ca98684157a0) --- scripts/suse/dpkg/99dpkgnotify | 1 + scripts/suse/dpkg/README.md | 9 +++++++ scripts/suse/dpkg/dpkgnotify | 44 ++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 scripts/suse/dpkg/99dpkgnotify create mode 100644 scripts/suse/dpkg/README.md create mode 100644 scripts/suse/dpkg/dpkgnotify diff --git a/scripts/suse/dpkg/99dpkgnotify b/scripts/suse/dpkg/99dpkgnotify new file mode 100644 index 0000000000..8013387a57 --- /dev/null +++ b/scripts/suse/dpkg/99dpkgnotify @@ -0,0 +1 @@ +DPkg::Post-Invoke {"/usr/bin/dpkgnotify";}; diff --git a/scripts/suse/dpkg/README.md b/scripts/suse/dpkg/README.md new file mode 100644 index 0000000000..b7a75c4786 --- /dev/null +++ b/scripts/suse/dpkg/README.md @@ -0,0 +1,9 @@ +## What it is + +Debian base package to notify installation of new packages outside the control of salt. + +## Installation +This script depends on python package, so python3 should be installed on the machine + +- The 99dpkgnotify file must be installed in /etc/apt/apt.conf.d/99dpkgnotify +- The dpkgnotify file must be installed in /usr/bin/dpkgnotify diff --git a/scripts/suse/dpkg/dpkgnotify b/scripts/suse/dpkg/dpkgnotify new file mode 100644 index 0000000000..d3ad3d2ba9 --- /dev/null +++ b/scripts/suse/dpkg/dpkgnotify @@ -0,0 +1,44 @@ +#!/usr/bin/python3 + +import os +import hashlib + +CK_PATH = "/var/cache/salt/minion/dpkg.cookie" +DPKG_PATH = "/var/lib/dpkg/status" + +def _get_mtime(): + """ + Get the modified time of the Package Database. + Returns: + Unix ticks + """ + return os.path.exists(DPKG_PATH) and int(os.path.getmtime(DPKG_PATH)) or 0 + + +def _get_checksum(): + """ + Get the checksum of the Package Database. + Returns: + hexdigest + """ + digest = hashlib.sha256() + with open(DPKG_PATH, "rb") as pkg_db_fh: + while True: + buff = pkg_db_fh.read(0x1000) + if not buff: + break + digest.update(buff) + return digest.hexdigest() + + +def dpkg_post_invoke(): + """ + Hook after the package installation transaction. + """ + if 'SALT_RUNNING' not in os.environ: + with open(CK_PATH, 'w') as ck_fh: + ck_fh.write('{chksum} {mtime}\n'.format(chksum=_get_checksum(), mtime=_get_mtime())) + + +if __name__ == "__main__": + dpkg_post_invoke() -- 2.30.2 ++++++ regression-fix-of-salt-ssh-on-processing-targets-353.patch ++++++ >From 543969c927df2f73662ac6ac19467d66d33e0577 Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <[email protected]> Date: Tue, 13 Apr 2021 18:00:42 +0300 Subject: [PATCH] Regression fix of salt-ssh on processing targets (#353) --- salt/client/ssh/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py index 1edb7b3b23..0a76627fe3 100644 --- a/salt/client/ssh/__init__.py +++ b/salt/client/ssh/__init__.py @@ -435,8 +435,6 @@ class SSH: self.opts["tgt"] = _hosts elif _hosts: self.opts["tgt"] = _hosts[0] - else: - self.opts["tgt"] = "" def get_pubkey(self): """ -- 2.30.2 ++++++ update-target-fix-for-salt-ssh-to-process-targets-li.patch ++++++ >From a603d31c4d3ace3590952ef848f4244c41abe7c8 Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <[email protected]> Date: Fri, 9 Apr 2021 16:01:32 +0300 Subject: [PATCH] Update target fix for salt-ssh to process targets list (bsc#1179831) (#336) * Update target fix for salt-ssh to process targets list (bsc#1179831) * Improvement for fixing (bsc#1179831) --- salt/client/ssh/__init__.py | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py index 6570fba5b1..1edb7b3b23 100644 --- a/salt/client/ssh/__init__.py +++ b/salt/client/ssh/__init__.py @@ -325,7 +325,7 @@ class SSH: if not self.opts.get("ssh_cli_tgt"): self.opts["ssh_cli_tgt"] = self.opts.get("tgt", "") hostname = self.opts.get("ssh_cli_tgt", "") - if "@" in hostname: + if isinstance(hostname, str) and "@" in hostname: user, hostname = hostname.split("@", 1) else: user = self.opts.get("ssh_user") @@ -376,7 +376,7 @@ class SSH: self.__parsed_rosters[self.ROSTER_UPDATE_FLAG] = False return - def _update_roster(self): + def _update_roster(self, hostname=None, user=None): """ Update default flat roster with the passed in information. :return: @@ -391,8 +391,8 @@ class SSH: "\n passwd: {passwd}\n".format( s_user=getpass.getuser(), s_time=datetime.datetime.utcnow().isoformat(), - hostname=self.opts.get("tgt", ""), - user=self.opts.get("ssh_user", ""), + hostname=hostname if hostname else self.opts.get("tgt", ""), + user=user if user else self.opts.get("ssh_user", ""), passwd=self.opts.get("ssh_passwd", ""), ) ) @@ -409,20 +409,34 @@ class SSH: Uptade targets in case hostname was directly passed without the roster. :return: """ - hostname = self.parse_tgt["hostname"] + hosts = self.parse_tgt["hostname"] user = self.parse_tgt["user"] - if hostname == "*": - hostname = "" - - if salt.utils.network.is_reachable_host(hostname): - self.opts["tgt"] = hostname - self.targets[hostname] = { - "passwd": self.opts.get("ssh_passwd", ""), - "host": hostname, - "user": user, - } - if self.opts.get("ssh_update_roster"): - self._update_roster() + + if not isinstance(hosts, (list, tuple)): + hosts = list([hosts]) + _hosts = list() + for hostname in hosts: + _user = user + if "@" in hostname: + _user, hostname = hostname.split("@", 1) + if hostname == "*": + continue + if salt.utils.network.is_reachable_host(hostname): + _hosts.append(hostname) + self.targets[hostname] = { + "passwd": self.opts.get("ssh_passwd", ""), + "host": hostname, + "user": _user, + } + if self.opts.get("ssh_update_roster"): + self._update_roster(hostname=hostname, user=_user) + + if self.tgt_type == "list": + self.opts["tgt"] = _hosts + elif _hosts: + self.opts["tgt"] = _hosts[0] + else: + self.opts["tgt"] = "" def get_pubkey(self): """ -- 2.31.1
