SHA256
1
0
forked from pool/salt

Accepting request 709997 from systemsmanagement:saltstack

- Fix zypper pkg.list_pkgs test expectation and dpkg mocking
- Added:
  * fix-zypper-pkg.list_pkgs-expectation-and-dpkg-mockin.patch 

- Set 'salt' group for files and directories created by
  salt-standalone-formulas-configuration package
- Various fixes for virt module
- Fix virt.volume_infos raising an exception when there is only virtual machine on the minion.
- Fix virt.purge() on all non-KVM hypervisors. For instance on Xen, virt.purge would simply throw an exception about unsupported flag
- Building a libvirt pool starts it. When defining a new pool, we need to
let build start it or we will get libvirt errors.
- Fix handling of Virtual Machines with white space in their name.
- Added:
  * virt.pool_running-fix-pool-start.patch
  * virt-handle-whitespaces-in-vm-names.patch
  * virt.volume_infos-fix-for-single-vm.patch
  * try-except-undefineflags-as-this-operation-is-not-su.patch

- avoid batch.py exception when minion does not respond (bsc#1135507)
- Added:
  * batch.py-avoid-exception-when-minion-does-not-respon.patch

- Preserve already defined DESTRUCTIVE_TESTS and EXPENSIVE_TESTS
  env variables
- Added:
  * preserve-already-defined-destructive_tests-and-expen.patch

- Do not break repo files with multiple line values on yumpkg (bsc#1135360)
- Added:
  * do-not-break-repo-files-with-multiple-line-values-on.patch

OBS-URL: https://build.opensuse.org/request/show/709997
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/salt?expand=0&rev=87
This commit is contained in:
Dominique Leuenberger 2019-06-18 12:55:12 +00:00 committed by Git OBS Bridge
commit 6c99feee82
11 changed files with 1272 additions and 5 deletions

View File

@ -1 +1 @@
6ea034113af0bc6f97110175d633bdf951af0fcd
1d301081a6e8a705499eb861b24c46ab17120691

View File

@ -0,0 +1,42 @@
From 50377852ca989ffa141fcf32d5ca57d120b455b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Guilherme=20Vanz?= <jvanz@jvanz.com>
Date: Tue, 21 May 2019 16:13:18 -0300
Subject: [PATCH] batch.py: avoid exception when minion does not respond
(bsc#1135507)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We have several issues reporting that salt is throwing exception when
the minion does not respond. This change avoid the exception adding a
default data to the minion when it fails to respond. This patch based
on the patch suggested by @roskens.
Issues #46876 #48509 #50238
bsc#1135507
Signed-off-by: José Guilherme Vanz <jguilhermevanz@suse.com>
---
salt/cli/batch.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/salt/cli/batch.py b/salt/cli/batch.py
index ce239215cb..1623fc5be8 100644
--- a/salt/cli/batch.py
+++ b/salt/cli/batch.py
@@ -315,6 +315,11 @@ class Batch(object):
if self.opts.get('failhard') and data['ret']['retcode'] > 0:
failhard = True
+ # avoid an exception if the minion does not respond.
+ if data.get("failed") is True:
+ log.debug('Minion %s failed to respond: data=%s', minion, data)
+ data = {'ret': 'Minion did not return. [Failed]', 'retcode': salt.defaults.exitcodes.EX_GENERIC}
+
if self.opts.get('raw'):
ret[minion] = data
yield data
--
2.21.0

View File

@ -0,0 +1,111 @@
From b99e55aab52d086315d54cf44af68f40dcf79dc9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Wed, 29 May 2019 11:03:16 +0100
Subject: [PATCH] Do not break repo files with multiple line values on
yumpkg (bsc#1135360)
---
salt/modules/yumpkg.py | 16 ++++++---
tests/integration/modules/test_pkg.py | 48 +++++++++++++++++++++++++++
2 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py
index 5ec3835574..3a4fe47a45 100644
--- a/salt/modules/yumpkg.py
+++ b/salt/modules/yumpkg.py
@@ -2763,7 +2763,12 @@ def del_repo(repo, basedir=None, **kwargs): # pylint: disable=W0613
del filerepos[stanza]['comments']
content += '\n[{0}]'.format(stanza)
for line in filerepos[stanza]:
- content += '\n{0}={1}'.format(line, filerepos[stanza][line])
+ # A whitespace is needed at the begining of the new line in order
+ # to avoid breaking multiple line values allowed on repo files.
+ value = filerepos[stanza][line]
+ if isinstance(value, six.string_types) and '\n' in value:
+ value = '\n '.join(value.split('\n'))
+ content += '\n{0}={1}'.format(line, value)
content += '\n{0}\n'.format(comments)
with salt.utils.files.fopen(repofile, 'w') as fileout:
@@ -2898,11 +2903,14 @@ def mod_repo(repo, basedir=None, **kwargs):
)
content += '[{0}]\n'.format(stanza)
for line in six.iterkeys(filerepos[stanza]):
+ # A whitespace is needed at the begining of the new line in order
+ # to avoid breaking multiple line values allowed on repo files.
+ value = filerepos[stanza][line]
+ if isinstance(value, six.string_types) and '\n' in value:
+ value = '\n '.join(value.split('\n'))
content += '{0}={1}\n'.format(
line,
- filerepos[stanza][line]
- if not isinstance(filerepos[stanza][line], bool)
- else _bool_to_str(filerepos[stanza][line])
+ value if not isinstance(value, bool) else _bool_to_str(value)
)
content += comments + '\n'
diff --git a/tests/integration/modules/test_pkg.py b/tests/integration/modules/test_pkg.py
index 0271cea81f..a82c9662c7 100644
--- a/tests/integration/modules/test_pkg.py
+++ b/tests/integration/modules/test_pkg.py
@@ -123,6 +123,54 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin):
if repo is not None:
self.run_function('pkg.del_repo', [repo])
+ def test_mod_del_repo_multiline_values(self):
+ '''
+ test modifying and deleting a software repository defined with multiline values
+ '''
+ os_grain = self.run_function('grains.item', ['os'])['os']
+ repo = None
+ try:
+ if os_grain in ['CentOS', 'RedHat', 'SUSE']:
+ my_baseurl = 'http://my.fake.repo/foo/bar/\n http://my.fake.repo.alt/foo/bar/'
+ expected_get_repo_baseurl = 'http://my.fake.repo/foo/bar/\nhttp://my.fake.repo.alt/foo/bar/'
+ major_release = int(
+ self.run_function(
+ 'grains.item',
+ ['osmajorrelease']
+ )['osmajorrelease']
+ )
+ repo = 'fakerepo'
+ name = 'Fake repo for RHEL/CentOS/SUSE'
+ baseurl = my_baseurl
+ gpgkey = 'https://my.fake.repo/foo/bar/MY-GPG-KEY.pub'
+ failovermethod = 'priority'
+ gpgcheck = 1
+ enabled = 1
+ ret = self.run_function(
+ 'pkg.mod_repo',
+ [repo],
+ name=name,
+ baseurl=baseurl,
+ gpgkey=gpgkey,
+ gpgcheck=gpgcheck,
+ enabled=enabled,
+ failovermethod=failovermethod,
+ )
+ # return data from pkg.mod_repo contains the file modified at
+ # the top level, so use next(iter(ret)) to get that key
+ self.assertNotEqual(ret, {})
+ repo_info = ret[next(iter(ret))]
+ self.assertIn(repo, repo_info)
+ self.assertEqual(repo_info[repo]['baseurl'], my_baseurl)
+ ret = self.run_function('pkg.get_repo', [repo])
+ self.assertEqual(ret['baseurl'], expected_get_repo_baseurl)
+ self.run_function('pkg.mod_repo', [repo])
+ ret = self.run_function('pkg.get_repo', [repo])
+ self.assertEqual(ret['baseurl'], expected_get_repo_baseurl)
+ finally:
+ if repo is not None:
+ self.run_function('pkg.del_repo', [repo])
+
@requires_salt_modules('pkg.owner')
def test_owner(self):
'''
--
2.21.0

View File

@ -0,0 +1,57 @@
From 8c4066c668147b1180c56f39722d2ade78ffd41c Mon Sep 17 00:00:00 2001
From: Mihai Dinca <mdinca@suse.de>
Date: Thu, 13 Jun 2019 17:48:55 +0200
Subject: [PATCH] Fix zypper pkg.list_pkgs expectation and dpkg mocking
---
tests/unit/modules/test_dpkg_lowpkg.py | 12 ++++++------
tests/unit/modules/test_zypperpkg.py | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/tests/unit/modules/test_dpkg_lowpkg.py b/tests/unit/modules/test_dpkg_lowpkg.py
index d16ce3cc1a..98557a1d10 100644
--- a/tests/unit/modules/test_dpkg_lowpkg.py
+++ b/tests/unit/modules/test_dpkg_lowpkg.py
@@ -127,9 +127,9 @@ class DpkgTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}):
self.assertEqual(dpkg.file_dict('httpd'), 'Error: error')
- @patch('salt.modules.dpkg._get_pkg_ds_avail', MagicMock(return_value=dselect_pkg))
- @patch('salt.modules.dpkg._get_pkg_info', MagicMock(return_value=pkgs_info))
- @patch('salt.modules.dpkg._get_pkg_license', MagicMock(return_value='BSD v3'))
+ @patch('salt.modules.dpkg_lowpkg._get_pkg_ds_avail', MagicMock(return_value=dselect_pkg))
+ @patch('salt.modules.dpkg_lowpkg._get_pkg_info', MagicMock(return_value=pkgs_info))
+ @patch('salt.modules.dpkg_lowpkg._get_pkg_license', MagicMock(return_value='BSD v3'))
def test_info(self):
'''
Test info
@@ -154,9 +154,9 @@ class DpkgTestCase(TestCase, LoaderModuleMockMixin):
assert pkg_data['maintainer'] == 'Simpsons Developers <simpsons-devel-discuss@lists.springfield.org>'
assert pkg_data['license'] == 'BSD v3'
- @patch('salt.modules.dpkg._get_pkg_ds_avail', MagicMock(return_value=dselect_pkg))
- @patch('salt.modules.dpkg._get_pkg_info', MagicMock(return_value=pkgs_info))
- @patch('salt.modules.dpkg._get_pkg_license', MagicMock(return_value='BSD v3'))
+ @patch('salt.modules.dpkg_lowpkg._get_pkg_ds_avail', MagicMock(return_value=dselect_pkg))
+ @patch('salt.modules.dpkg_lowpkg._get_pkg_info', MagicMock(return_value=pkgs_info))
+ @patch('salt.modules.dpkg_lowpkg._get_pkg_license', MagicMock(return_value='BSD v3'))
def test_info_attr(self):
'''
Test info with 'attr' parameter
diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py
index 5c5091a570..a7063e47c6 100644
--- a/tests/unit/modules/test_zypperpkg.py
+++ b/tests/unit/modules/test_zypperpkg.py
@@ -659,7 +659,7 @@ Repository 'DUMMY' not found by its alias, number, or URI.
'install_date_time_t': 1503572639,
'epoch': None,
}],
- 'perseus-dummy.i586': [{
+ 'perseus-dummy': [{
'version': '1.1',
'release': '1.1',
'arch': 'i586',
--
2.21.0

View File

@ -0,0 +1,34 @@
From 5a1e0b7b8eab900e03fa800cc7a0a2b59bf2ff55 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Mon, 3 Jun 2019 11:38:36 +0100
Subject: [PATCH] Preserve already defined DESTRUCTIVE_TESTS and
EXPENSIVE_TESTS env variables
---
tests/support/parser/__init__.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/support/parser/__init__.py b/tests/support/parser/__init__.py
index ed262d46c0..f269457670 100644
--- a/tests/support/parser/__init__.py
+++ b/tests/support/parser/__init__.py
@@ -574,12 +574,12 @@ class SaltTestingParser(optparse.OptionParser):
self.validate_options()
- if self.support_destructive_tests_selection:
+ if self.support_destructive_tests_selection and not os.environ.get('DESTRUCTIVE_TESTS', None):
# Set the required environment variable in order to know if
# destructive tests should be executed or not.
os.environ['DESTRUCTIVE_TESTS'] = str(self.options.run_destructive)
- if self.support_expensive_tests_selection:
+ if self.support_expensive_tests_selection and not os.environ.get('EXPENSIVE_TESTS', None):
# Set the required environment variable in order to know if
# expensive tests should be executed or not.
os.environ['EXPENSIVE_TESTS'] = str(self.options.run_expensive)
--
2.17.1

View File

@ -1,3 +1,54 @@
-------------------------------------------------------------------
Fri Jun 14 14:09:29 UTC 2019 - Pablo Suárez Hernández <pablo.suarezhernandez@suse.com>
- Fix zypper pkg.list_pkgs test expectation and dpkg mocking
- Added:
* fix-zypper-pkg.list_pkgs-expectation-and-dpkg-mockin.patch
-------------------------------------------------------------------
Fri Jun 14 12:15:43 UTC 2019 - Pablo Suárez Hernández <pablo.suarezhernandez@suse.com>
- Set 'salt' group for files and directories created by
salt-standalone-formulas-configuration package
- Various fixes for virt module
- Fix virt.volume_infos raising an exception when there is only virtual machine on the minion.
- Fix virt.purge() on all non-KVM hypervisors. For instance on Xen, virt.purge would simply throw an exception about unsupported flag
- Building a libvirt pool starts it. When defining a new pool, we need to
let build start it or we will get libvirt errors.
- Fix handling of Virtual Machines with white space in their name.
- Added:
* virt.pool_running-fix-pool-start.patch
* virt-handle-whitespaces-in-vm-names.patch
* virt.volume_infos-fix-for-single-vm.patch
* try-except-undefineflags-as-this-operation-is-not-su.patch
-------------------------------------------------------------------
Wed Jun 5 14:26:29 UTC 2019 - Pablo Suárez Hernández <pablo.suarezhernandez@suse.com>
- avoid batch.py exception when minion does not respond (bsc#1135507)
- Added:
* batch.py-avoid-exception-when-minion-does-not-respon.patch
-------------------------------------------------------------------
Mon Jun 3 11:01:57 UTC 2019 - psuarezhernandez@suse.com
- Preserve already defined DESTRUCTIVE_TESTS and EXPENSIVE_TESTS
env variables
- Added:
* preserve-already-defined-destructive_tests-and-expen.patch
-------------------------------------------------------------------
Wed May 29 10:54:42 UTC 2019 - psuarezhernandez@suse.com
- Do not break repo files with multiple line values on yumpkg (bsc#1135360)
- Added:
* do-not-break-repo-files-with-multiple-line-values-on.patch
-------------------------------------------------------------------
Fri May 24 16:03:09 UTC 2019 - psuarezhernandez@suse.com

View File

@ -180,6 +180,22 @@ Patch55: switch-firewalld-state-to-use-change_interface.patch
Patch56: add-standalone-configuration-file-for-enabling-packa.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/53237
Patch57: add-ppc64le-as-a-valid-rpm-package-architecture.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/53293
Patch58: do-not-break-repo-files-with-multiple-line-values-on.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/53343
Patch59: preserve-already-defined-destructive_tests-and-expen.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/53159
Patch60: batch.py-avoid-exception-when-minion-does-not-respon.patch
# PATCH_FIX_UPSTREAM: https://github.com/saltstack/salt/pull/53471
Patch61: fix-zypper-pkg.list_pkgs-expectation-and-dpkg-mockin.patch
# PATCH_FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/142
Patch62: try-except-undefineflags-as-this-operation-is-not-su.patch
# PATCH_FIX_UPSTREAM: https://github.com/saltstack/salt/pull/52160
Patch63: virt-handle-whitespaces-in-vm-names.patch
# PATCH_FIX_UPSTREAM: https://github.com/saltstack/salt/pull/52341
Patch64: virt.pool_running-fix-pool-start.patch
# PATCH_FIX_UPSTREAM: https://github.com/saltstack/salt/pull/52414
Patch65: virt.volume_infos-fix-for-single-vm.patch
# BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRoot: %{_tmppath}/%{name}-%{version}-build
@ -707,6 +723,14 @@ cp %{S:5} ./.travis.yml
%patch55 -p1
%patch56 -p1
%patch57 -p1
%patch58 -p1
%patch59 -p1
%patch60 -p1
%patch61 -p1
%patch62 -p1
%patch63 -p1
%patch64 -p1
%patch65 -p1
%build
%if 0%{?build_py2}
@ -1426,10 +1450,10 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version
%files standalone-formulas-configuration
%defattr(-,root,root)
%config(noreplace) %attr(0640, root, root) %{_sysconfdir}/salt/master.d/standalone-formulas-configuration.conf
%dir %attr(0750, root, root) %{_prefix}/share/salt-formulas/
%dir %attr(0750, root, root) %{_prefix}/share/salt-formulas/states/
%dir %attr(0750, root, root) %{_prefix}/share/salt-formulas/metadata/
%config(noreplace) %attr(0640, root, salt) %{_sysconfdir}/salt/master.d/standalone-formulas-configuration.conf
%dir %attr(0750, root, salt) %{_prefix}/share/salt-formulas/
%dir %attr(0750, root, salt) %{_prefix}/share/salt-formulas/states/
%dir %attr(0750, root, salt) %{_prefix}/share/salt-formulas/metadata/
%changelog

View File

@ -0,0 +1,31 @@
From e0bded83fa691c3b972fa4c22b14c5ac0a7a3f13 Mon Sep 17 00:00:00 2001
From: Jeroen Schutrup <jeroenschutrup@hotmail.nl>
Date: Sun, 12 Aug 2018 19:43:22 +0200
Subject: [PATCH] Try/except undefineFlags() as this operation is not
supported on bhyve
(cherry picked from commit 29a44aceb1a73347ac07dd241b4a64a4a38cef6e)
---
salt/modules/virt.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index a3f625909d..423016cd90 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -3189,7 +3189,10 @@ def purge(vm_, dirs=False, removables=None, **kwargs):
shutil.rmtree(dir_)
if getattr(libvirt, 'VIR_DOMAIN_UNDEFINE_NVRAM', False):
# This one is only in 1.2.8+
- dom.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_NVRAM)
+ try:
+ dom.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_NVRAM)
+ except Exception:
+ dom.undefine()
else:
dom.undefine()
conn.close()
--
2.21.0

View File

@ -0,0 +1,243 @@
From fbad82a38b4460260726cb3b9456cad7986eb4cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= <cbosdonnat@suse.com>
Date: Wed, 13 Mar 2019 09:43:51 +0100
Subject: [PATCH] virt: handle whitespaces in VM names
The disk creation code is now ready to handle whitespaces in virtual
machine name.
---
salt/modules/virt.py | 8 +++---
tests/unit/modules/test_virt.py | 46 ++++++++++++++++-----------------
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index 423016cd90..d160f0905f 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -760,14 +760,14 @@ def _qemu_image_create(disk, create_overlay=False, saltenv='base'):
qcow2 = False
if salt.utils.path.which('qemu-img'):
- res = __salt__['cmd.run']('qemu-img info {}'.format(sfn))
+ res = __salt__['cmd.run']('qemu-img info "{}"'.format(sfn))
imageinfo = salt.utils.yaml.safe_load(res)
qcow2 = imageinfo['file format'] == 'qcow2'
try:
if create_overlay and qcow2:
log.info('Cloning qcow2 image %s using copy on write', sfn)
__salt__['cmd.run'](
- 'qemu-img create -f qcow2 -o backing_file={0} {1}'
+ 'qemu-img create -f qcow2 -o backing_file="{0}" "{1}"'
.format(sfn, img_dest).split())
else:
log.debug('Copying %s to %s', sfn, img_dest)
@@ -778,7 +778,7 @@ def _qemu_image_create(disk, create_overlay=False, saltenv='base'):
if disk_size and qcow2:
log.debug('Resize qcow2 image to %sM', disk_size)
__salt__['cmd.run'](
- 'qemu-img resize {0} {1}M'
+ 'qemu-img resize "{0}" {1}M'
.format(img_dest, disk_size)
)
@@ -800,7 +800,7 @@ def _qemu_image_create(disk, create_overlay=False, saltenv='base'):
if disk_size:
log.debug('Create empty image with size %sM', disk_size)
__salt__['cmd.run'](
- 'qemu-img create -f {0} {1} {2}M'
+ 'qemu-img create -f {0} "{1}" {2}M'
.format(disk.get('format', 'qcow2'), img_dest, disk_size)
)
else:
diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py
index bbe8d813d7..cc62b67918 100644
--- a/tests/unit/modules/test_virt.py
+++ b/tests/unit/modules/test_virt.py
@@ -1106,7 +1106,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(virt.__salt__, {'cmd.run': mock_run}): # pylint: disable=no-member
# Ensure the init() function allows creating VM without NIC and disk
- virt.init('testvm',
+ virt.init('test vm',
2,
1234,
nic=None,
@@ -1120,7 +1120,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
# Test case creating disks
defineMock.reset_mock()
mock_run.reset_mock()
- virt.init('testvm',
+ virt.init('test vm',
2,
1234,
nic=None,
@@ -1134,10 +1134,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
definition = ET.fromstring(defineMock.call_args_list[0][0][0])
disk_sources = [disk.find('source').get('file') if disk.find('source') is not None else None
for disk in definition.findall('./devices/disk')]
- expected_disk_path = os.path.join(root_dir, 'testvm_system.qcow2')
+ expected_disk_path = os.path.join(root_dir, 'test vm_system.qcow2')
self.assertEqual(disk_sources, [expected_disk_path, None])
self.assertEqual(mock_run.call_args[0][0],
- 'qemu-img create -f qcow2 {0} 10240M'.format(expected_disk_path))
+ 'qemu-img create -f qcow2 "{0}" 10240M'.format(expected_disk_path))
self.assertEqual(mock_chmod.call_args[0][0], expected_disk_path)
def test_update(self):
@@ -1147,7 +1147,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
root_dir = os.path.join(salt.syspaths.ROOT_DIR, 'srv', 'salt-images')
xml = '''
<domain type='kvm' id='7'>
- <name>myvm</name>
+ <name>my vm</name>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>1048576</currentMemory>
<vcpu placement='auto'>1</vcpu>
@@ -1157,7 +1157,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
<devices>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
- <source file='{0}{1}myvm_system.qcow2'/>
+ <source file='{0}{1}my vm_system.qcow2'/>
<backingStore/>
<target dev='vda' bus='virtio'/>
<alias name='virtio-disk0'/>
@@ -1165,7 +1165,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
</disk>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
- <source file='{0}{1}myvm_data.qcow2'/>
+ <source file='{0}{1}my vm_data.qcow2'/>
<backingStore/>
<target dev='vdb' bus='virtio'/>
<alias name='virtio-disk1'/>
@@ -1198,7 +1198,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
</devices>
</domain>
'''.format(root_dir, os.sep)
- domain_mock = self.set_mock_vm('myvm', xml)
+ domain_mock = self.set_mock_vm('my vm', xml)
domain_mock.OSType = MagicMock(return_value='hvm')
define_mock = MagicMock(return_value=True)
self.mock_conn.defineXML = define_mock
@@ -1211,7 +1211,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
'cpu': True,
'disk': {'attached': [], 'detached': []},
'interface': {'attached': [], 'detached': []}
- }, virt.update('myvm', cpu=2))
+ }, virt.update('my vm', cpu=2))
setxml = ET.fromstring(define_mock.call_args[0][0])
self.assertEqual(setxml.find('vcpu').text, '2')
self.assertEqual(setvcpus_mock.call_args[0][0], 2)
@@ -1225,7 +1225,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
'mem': True,
'disk': {'attached': [], 'detached': []},
'interface': {'attached': [], 'detached': []}
- }, virt.update('myvm', mem=2048))
+ }, virt.update('my vm', mem=2048))
setxml = ET.fromstring(define_mock.call_args[0][0])
self.assertEqual(setxml.find('memory').text, '2048')
self.assertEqual(setxml.find('memory').get('unit'), 'MiB')
@@ -1240,21 +1240,21 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
mock_run = MagicMock()
with patch.dict(os.__dict__, {'chmod': mock_chmod, 'makedirs': MagicMock()}): # pylint: disable=no-member
with patch.dict(virt.__salt__, {'cmd.run': mock_run}): # pylint: disable=no-member
- ret = virt.update('myvm', disk_profile='default', disks=[
+ ret = virt.update('my vm', disk_profile='default', disks=[
{'name': 'cddrive', 'device': 'cdrom', 'source_file': None, 'model': 'ide'},
{'name': 'added', 'size': 2048}])
added_disk_path = os.path.join(
- virt.__salt__['config.get']('virt:images'), 'myvm_added.qcow2') # pylint: disable=no-member
+ virt.__salt__['config.get']('virt:images'), 'my vm_added.qcow2') # pylint: disable=no-member
self.assertEqual(mock_run.call_args[0][0],
- 'qemu-img create -f qcow2 {0} 2048M'.format(added_disk_path))
+ 'qemu-img create -f qcow2 "{0}" 2048M'.format(added_disk_path))
self.assertEqual(mock_chmod.call_args[0][0], added_disk_path)
self.assertListEqual(
- [None, os.path.join(root_dir, 'myvm_added.qcow2')],
+ [None, os.path.join(root_dir, 'my vm_added.qcow2')],
[ET.fromstring(disk).find('source').get('file') if str(disk).find('<source') > -1 else None
for disk in ret['disk']['attached']])
self.assertListEqual(
- [os.path.join(root_dir, 'myvm_data.qcow2')],
+ [os.path.join(root_dir, 'my vm_data.qcow2')],
[ET.fromstring(disk).find('source').get('file') for disk in ret['disk']['detached']])
self.assertEqual(devattach_mock.call_count, 2)
devdetach_mock.assert_called_once()
@@ -1271,7 +1271,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
devattach_mock.reset_mock()
devdetach_mock.reset_mock()
with patch.dict(salt.modules.config.__opts__, mock_config): # pylint: disable=no-member
- ret = virt.update('myvm', nic_profile='myprofile',
+ ret = virt.update('my vm', nic_profile='myprofile',
interfaces=[{'name': 'eth0', 'type': 'network', 'source': 'default',
'mac': '52:54:00:39:02:b1'},
{'name': 'eth1', 'type': 'network', 'source': 'newnet'}])
@@ -1285,7 +1285,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
# Remove nics case
devattach_mock.reset_mock()
devdetach_mock.reset_mock()
- ret = virt.update('myvm', nic_profile=None, interfaces=[])
+ ret = virt.update('my vm', nic_profile=None, interfaces=[])
self.assertEqual([], ret['interface']['attached'])
self.assertEqual(2, len(ret['interface']['detached']))
devattach_mock.assert_not_called()
@@ -1294,7 +1294,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
# Remove disks case (yeah, it surely is silly)
devattach_mock.reset_mock()
devdetach_mock.reset_mock()
- ret = virt.update('myvm', disk_profile=None, disks=[])
+ ret = virt.update('my vm', disk_profile=None, disks=[])
self.assertEqual([], ret['disk']['attached'])
self.assertEqual(2, len(ret['disk']['detached']))
devattach_mock.assert_not_called()
@@ -1305,7 +1305,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
'definition': True,
'disk': {'attached': [], 'detached': []},
'interface': {'attached': [], 'detached': []}
- }, virt.update('myvm', graphics={'type': 'vnc'}))
+ }, virt.update('my vm', graphics={'type': 'vnc'}))
setxml = ET.fromstring(define_mock.call_args[0][0])
self.assertEqual('vnc', setxml.find('devices/graphics').get('type'))
@@ -1314,7 +1314,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
'definition': False,
'disk': {'attached': [], 'detached': []},
'interface': {'attached': [], 'detached': []}
- }, virt.update('myvm', cpu=1, mem=1024,
+ }, virt.update('my vm', cpu=1, mem=1024,
disk_profile='default', disks=[{'name': 'data', 'size': 2048}],
nic_profile='myprofile',
interfaces=[{'name': 'eth0', 'type': 'network', 'source': 'default',
@@ -1328,7 +1328,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
self.mock_conn.defineXML.side_effect = self.mock_libvirt.libvirtError("Test error")
setmem_mock.reset_mock()
with self.assertRaises(self.mock_libvirt.libvirtError):
- virt.update('myvm', mem=2048)
+ virt.update('my vm', mem=2048)
# Failed single update failure case
self.mock_conn.defineXML = MagicMock(return_value=True)
@@ -1338,7 +1338,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
'errors': ['Failed to live change memory'],
'disk': {'attached': [], 'detached': []},
'interface': {'attached': [], 'detached': []}
- }, virt.update('myvm', mem=2048))
+ }, virt.update('my vm', mem=2048))
# Failed multiple updates failure case
self.assertEqual({
@@ -1347,7 +1347,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
'cpu': True,
'disk': {'attached': [], 'detached': []},
'interface': {'attached': [], 'detached': []}
- }, virt.update('myvm', cpu=4, mem=2048))
+ }, virt.update('my vm', cpu=4, mem=2048))
def test_mixed_dict_and_list_as_profile_objects(self):
'''
--
2.21.0

View File

@ -0,0 +1,582 @@
From 946dd98e911e62c7bc3bcdd8adc8a170645c981c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= <cbosdonnat@suse.com>
Date: Wed, 6 Jun 2018 09:49:36 +0200
Subject: [PATCH] virt.pool_running: fix pool start
Building a libvirt pool starts it. When defining a new pool, we need to
let build start it or we will get libvirt errors.
Also backport virt states test to add test for the bug:
cherry picked from commits:
- 451e7da55bd232546c4d30ec36d432de2d5a14ec
- 495db345a570cb14cd9b0ae96e1bb0f3fad6aef0
- cb00a5f9b4c9a2a863da3c1107ca6458a4092c3d
- fc75872fb63e254eecc782168ff8b37157d9e514
- 2a5f6ae5d69be71daeab6c9cbe4dd642255ff3c6
- 2463ebe5a82b1a017004e8e0e390535485dc703e
- c7c5d6ee88fbc74d0ee0aeab41beb421d8625f05
---
salt/states/virt.py | 7 +-
tests/unit/states/test_virt.py | 508 ++++++++++++++++++++++++++++++++-
2 files changed, 508 insertions(+), 7 deletions(-)
diff --git a/salt/states/virt.py b/salt/states/virt.py
index 90693880df..d411f864cd 100644
--- a/salt/states/virt.py
+++ b/salt/states/virt.py
@@ -780,7 +780,7 @@ def pool_running(name,
source_name=(source or {}).get('name', None),
source_format=(source or {}).get('format', None),
transient=transient,
- start=True,
+ start=False,
connection=connection,
username=username,
password=password)
@@ -795,11 +795,6 @@ def pool_running(name,
connection=connection,
username=username,
password=password)
-
- __salt__['virt.pool_start'](name,
- connection=connection,
- username=username,
- password=password)
ret['changes'][name] = 'Pool defined and started'
ret['comment'] = 'Pool {0} defined and started'.format(name)
except libvirt.libvirtError as err:
diff --git a/tests/unit/states/test_virt.py b/tests/unit/states/test_virt.py
index 2e421319ad..8022989937 100644
--- a/tests/unit/states/test_virt.py
+++ b/tests/unit/states/test_virt.py
@@ -21,6 +21,25 @@ from tests.support.mock import (
# Import Salt Libs
import salt.states.virt as virt
import salt.utils.files
+from salt.exceptions import CommandExecutionError
+
+# Import 3rd-party libs
+from salt.ext import six
+
+
+class LibvirtMock(MagicMock): # pylint: disable=too-many-ancestors
+ '''
+ libvirt library mockup
+ '''
+ class libvirtError(Exception): # pylint: disable=invalid-name
+ '''
+ libvirt error mockup
+ '''
+ def get_error_message(self):
+ '''
+ Fake function return error message
+ '''
+ return six.text_type(self)
@skipIf(NO_MOCK, NO_MOCK_REASON)
@@ -29,7 +48,12 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
Test cases for salt.states.libvirt
'''
def setup_loader_modules(self):
- return {virt: {}}
+ self.mock_libvirt = LibvirtMock() # pylint: disable=attribute-defined-outside-init
+ self.addCleanup(delattr, self, 'mock_libvirt')
+ loader_globals = {
+ 'libvirt': self.mock_libvirt
+ }
+ return {virt: loader_globals}
@classmethod
def setUpClass(cls):
@@ -195,3 +219,485 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
locality='Los_Angeles',
organization='SaltStack',
expiration_days=700), ret)
+
+ def test_running(self):
+ '''
+ running state test cases.
+ '''
+ ret = {'name': 'myvm',
+ 'changes': {},
+ 'result': True,
+ 'comment': 'myvm is running'}
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(return_value='stopped'),
+ 'virt.start': MagicMock(return_value=0),
+ }):
+ ret.update({'changes': {'myvm': 'Domain started'},
+ 'comment': 'Domain myvm started'})
+ self.assertDictEqual(virt.running('myvm'), ret)
+
+ init_mock = MagicMock(return_value=True)
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
+ 'virt.init': init_mock,
+ 'virt.start': MagicMock(return_value=0)
+ }):
+ ret.update({'changes': {'myvm': 'Domain defined and started'},
+ 'comment': 'Domain myvm defined and started'})
+ self.assertDictEqual(virt.running('myvm',
+ cpu=2,
+ mem=2048,
+ image='/path/to/img.qcow2'), ret)
+ init_mock.assert_called_with('myvm', cpu=2, mem=2048, image='/path/to/img.qcow2',
+ os_type=None, arch=None,
+ disk=None, disks=None, nic=None, interfaces=None,
+ graphics=None, hypervisor=None,
+ seed=True, install=True, pub_key=None, priv_key=None,
+ connection=None, username=None, password=None)
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
+ 'virt.init': init_mock,
+ 'virt.start': MagicMock(return_value=0)
+ }):
+ ret.update({'changes': {'myvm': 'Domain defined and started'},
+ 'comment': 'Domain myvm defined and started'})
+ disks = [{
+ 'name': 'system',
+ 'size': 8192,
+ 'overlay_image': True,
+ 'pool': 'default',
+ 'image': '/path/to/image.qcow2'
+ },
+ {
+ 'name': 'data',
+ 'size': 16834
+ }]
+ ifaces = [{
+ 'name': 'eth0',
+ 'mac': '01:23:45:67:89:AB'
+ },
+ {
+ 'name': 'eth1',
+ 'type': 'network',
+ 'source': 'admin'
+ }]
+ graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
+ self.assertDictEqual(virt.running('myvm',
+ cpu=2,
+ mem=2048,
+ os_type='linux',
+ arch='i686',
+ vm_type='qemu',
+ disk_profile='prod',
+ disks=disks,
+ nic_profile='prod',
+ interfaces=ifaces,
+ graphics=graphics,
+ seed=False,
+ install=False,
+ pub_key='/path/to/key.pub',
+ priv_key='/path/to/key',
+ connection='someconnection',
+ username='libvirtuser',
+ password='supersecret'), ret)
+ init_mock.assert_called_with('myvm',
+ cpu=2,
+ mem=2048,
+ os_type='linux',
+ arch='i686',
+ image=None,
+ disk='prod',
+ disks=disks,
+ nic='prod',
+ interfaces=ifaces,
+ graphics=graphics,
+ hypervisor='qemu',
+ seed=False,
+ install=False,
+ pub_key='/path/to/key.pub',
+ priv_key='/path/to/key',
+ connection='someconnection',
+ username='libvirtuser',
+ password='supersecret')
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(return_value='stopped'),
+ 'virt.start': MagicMock(side_effect=[self.mock_libvirt.libvirtError('libvirt error msg')])
+ }):
+ ret.update({'changes': {}, 'result': False, 'comment': 'libvirt error msg'})
+ self.assertDictEqual(virt.running('myvm'), ret)
+
+ # Working update case when running
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(return_value='running'),
+ 'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
+ }):
+ ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
+ 'result': True,
+ 'comment': 'Domain myvm updated, restart to fully apply the changes'})
+ self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
+
+ # Working update case when stopped
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(return_value='stopped'),
+ 'virt.start': MagicMock(return_value=0),
+ 'virt.update': MagicMock(return_value={'definition': True})
+ }):
+ ret.update({'changes': {'myvm': 'Domain updated and started'},
+ 'result': True,
+ 'comment': 'Domain myvm updated and started'})
+ self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
+
+ # Failed live update case
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(return_value='running'),
+ 'virt.update': MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']})
+ }):
+ ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}},
+ 'result': True,
+ 'comment': 'Domain myvm updated, but some live update(s) failed'})
+ self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
+
+ # Failed definition update case
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.vm_state': MagicMock(return_value='running'),
+ 'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')])
+ }):
+ ret.update({'changes': {},
+ 'result': False,
+ 'comment': 'error message'})
+ self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
+
+ def test_stopped(self):
+ '''
+ stopped state test cases.
+ '''
+ ret = {'name': 'myvm',
+ 'changes': {},
+ 'result': True}
+
+ shutdown_mock = MagicMock(return_value=True)
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.shutdown': shutdown_mock
+ }):
+ ret.update({'changes': {
+ 'stopped': [{'domain': 'myvm', 'shutdown': True}]
+ },
+ 'comment': 'Machine has been shut down'})
+ self.assertDictEqual(virt.stopped('myvm'), ret)
+ shutdown_mock.assert_called_with('myvm', connection=None, username=None, password=None)
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.shutdown': shutdown_mock,
+ }):
+ self.assertDictEqual(virt.stopped('myvm',
+ connection='myconnection',
+ username='user',
+ password='secret'), ret)
+ shutdown_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret')
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.shutdown': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+ }):
+ ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
+ 'result': False,
+ 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.stopped('myvm'), ret)
+
+ with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
+ ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.stopped('myvm'), ret)
+
+ def test_powered_off(self):
+ '''
+ powered_off state test cases.
+ '''
+ ret = {'name': 'myvm',
+ 'changes': {},
+ 'result': True}
+
+ stop_mock = MagicMock(return_value=True)
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.stop': stop_mock
+ }):
+ ret.update({'changes': {
+ 'unpowered': [{'domain': 'myvm', 'stop': True}]
+ },
+ 'comment': 'Machine has been powered off'})
+ self.assertDictEqual(virt.powered_off('myvm'), ret)
+ stop_mock.assert_called_with('myvm', connection=None, username=None, password=None)
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.stop': stop_mock,
+ }):
+ self.assertDictEqual(virt.powered_off('myvm',
+ connection='myconnection',
+ username='user',
+ password='secret'), ret)
+ stop_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret')
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.stop': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+ }):
+ ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
+ 'result': False,
+ 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.powered_off('myvm'), ret)
+
+ with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
+ ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.powered_off('myvm'), ret)
+
+ def test_snapshot(self):
+ '''
+ snapshot state test cases.
+ '''
+ ret = {'name': 'myvm',
+ 'changes': {},
+ 'result': True}
+
+ snapshot_mock = MagicMock(return_value=True)
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.snapshot': snapshot_mock
+ }):
+ ret.update({'changes': {
+ 'saved': [{'domain': 'myvm', 'snapshot': True}]
+ },
+ 'comment': 'Snapshot has been taken'})
+ self.assertDictEqual(virt.snapshot('myvm'), ret)
+ snapshot_mock.assert_called_with('myvm', suffix=None, connection=None, username=None, password=None)
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.snapshot': snapshot_mock,
+ }):
+ self.assertDictEqual(virt.snapshot('myvm',
+ suffix='snap',
+ connection='myconnection',
+ username='user',
+ password='secret'), ret)
+ snapshot_mock.assert_called_with('myvm',
+ suffix='snap',
+ connection='myconnection',
+ username='user',
+ password='secret')
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.snapshot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+ }):
+ ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
+ 'result': False,
+ 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.snapshot('myvm'), ret)
+
+ with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
+ ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.snapshot('myvm'), ret)
+
+ def test_rebooted(self):
+ '''
+ rebooted state test cases.
+ '''
+ ret = {'name': 'myvm',
+ 'changes': {},
+ 'result': True}
+
+ reboot_mock = MagicMock(return_value=True)
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.reboot': reboot_mock
+ }):
+ ret.update({'changes': {
+ 'rebooted': [{'domain': 'myvm', 'reboot': True}]
+ },
+ 'comment': 'Machine has been rebooted'})
+ self.assertDictEqual(virt.rebooted('myvm'), ret)
+ reboot_mock.assert_called_with('myvm', connection=None, username=None, password=None)
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.reboot': reboot_mock,
+ }):
+ self.assertDictEqual(virt.rebooted('myvm',
+ connection='myconnection',
+ username='user',
+ password='secret'), ret)
+ reboot_mock.assert_called_with('myvm',
+ connection='myconnection',
+ username='user',
+ password='secret')
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
+ 'virt.reboot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+ }):
+ ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
+ 'result': False,
+ 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.rebooted('myvm'), ret)
+
+ with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
+ ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
+ self.assertDictEqual(virt.rebooted('myvm'), ret)
+
+ def test_network_running(self):
+ '''
+ network_running state test cases.
+ '''
+ ret = {'name': 'mynet', 'changes': {}, 'result': True, 'comment': ''}
+ define_mock = MagicMock(return_value=True)
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.network_info': MagicMock(return_value={}),
+ 'virt.network_define': define_mock
+ }):
+ ret.update({'changes': {'mynet': 'Network defined and started'},
+ 'comment': 'Network mynet defined and started'})
+ self.assertDictEqual(virt.network_running('mynet',
+ 'br2',
+ 'bridge',
+ vport='openvswitch',
+ tag=180,
+ autostart=False,
+ connection='myconnection',
+ username='user',
+ password='secret'), ret)
+ define_mock.assert_called_with('mynet',
+ 'br2',
+ 'bridge',
+ 'openvswitch',
+ tag=180,
+ autostart=False,
+ start=True,
+ connection='myconnection',
+ username='user',
+ password='secret')
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.network_info': MagicMock(return_value={'active': True}),
+ 'virt.network_define': define_mock,
+ }):
+ ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'})
+ self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
+
+ start_mock = MagicMock(return_value=True)
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.network_info': MagicMock(return_value={'active': False}),
+ 'virt.network_start': start_mock,
+ 'virt.network_define': define_mock,
+ }):
+ ret.update({'changes': {'mynet': 'Network started'}, 'comment': 'Network mynet started'})
+ self.assertDictEqual(virt.network_running('mynet',
+ 'br2',
+ 'bridge',
+ connection='myconnection',
+ username='user',
+ password='secret'), ret)
+ start_mock.assert_called_with('mynet', connection='myconnection', username='user', password='secret')
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.network_info': MagicMock(return_value={}),
+ 'virt.network_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+ }):
+ ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
+ self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
+
+ def test_pool_running(self):
+ '''
+ pool_running state test cases.
+ '''
+ ret = {'name': 'mypool', 'changes': {}, 'result': True, 'comment': ''}
+ mocks = {mock: MagicMock(return_value=True) for mock in ['define', 'autostart', 'build', 'start']}
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.pool_info': MagicMock(return_value={}),
+ 'virt.pool_define': mocks['define'],
+ 'virt.pool_build': mocks['build'],
+ 'virt.pool_start': mocks['start'],
+ 'virt.pool_set_autostart': mocks['autostart']
+ }):
+ ret.update({'changes': {'mypool': 'Pool defined and started'},
+ 'comment': 'Pool mypool defined and started'})
+ self.assertDictEqual(virt.pool_running('mypool',
+ ptype='logical',
+ target='/dev/base',
+ permissions={'mode': '0770',
+ 'owner': 1000,
+ 'group': 100,
+ 'label': 'seclabel'},
+ source={'devices': [{'path': '/dev/sda'}]},
+ transient=True,
+ autostart=True,
+ connection='myconnection',
+ username='user',
+ password='secret'), ret)
+ mocks['define'].assert_called_with('mypool',
+ ptype='logical',
+ target='/dev/base',
+ permissions={'mode': '0770',
+ 'owner': 1000,
+ 'group': 100,
+ 'label': 'seclabel'},
+ source_devices=[{'path': '/dev/sda'}],
+ source_dir=None,
+ source_adapter=None,
+ source_hosts=None,
+ source_auth=None,
+ source_name=None,
+ source_format=None,
+ transient=True,
+ start=False,
+ connection='myconnection',
+ username='user',
+ password='secret')
+ mocks['autostart'].assert_called_with('mypool',
+ state='on',
+ connection='myconnection',
+ username='user',
+ password='secret')
+ mocks['build'].assert_called_with('mypool',
+ connection='myconnection',
+ username='user',
+ password='secret')
+ mocks['start'].assert_not_called()
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.pool_info': MagicMock(return_value={'state': 'running'}),
+ }):
+ ret.update({'changes': {}, 'comment': 'Pool mypool exists and is running'})
+ self.assertDictEqual(virt.pool_running('mypool',
+ ptype='logical',
+ target='/dev/base',
+ source={'devices': [{'path': '/dev/sda'}]}), ret)
+
+ for mock in mocks:
+ mocks[mock].reset_mock()
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.pool_info': MagicMock(return_value={'state': 'stopped'}),
+ 'virt.pool_build': mocks['build'],
+ 'virt.pool_start': mocks['start']
+ }):
+ ret.update({'changes': {'mypool': 'Pool started'}, 'comment': 'Pool mypool started'})
+ self.assertDictEqual(virt.pool_running('mypool',
+ ptype='logical',
+ target='/dev/base',
+ source={'devices': [{'path': '/dev/sda'}]}), ret)
+ mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None)
+ mocks['build'].assert_not_called()
+
+ with patch.dict(virt.__salt__, { # pylint: disable=no-member
+ 'virt.pool_info': MagicMock(return_value={}),
+ 'virt.pool_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+ }):
+ ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
+ self.assertDictEqual(virt.pool_running('mypool',
+ ptype='logical',
+ target='/dev/base',
+ source={'devices': [{'path': '/dev/sda'}]}), ret)
--
2.21.0

View File

@ -0,0 +1,92 @@
From b0b5a78a463f7587be4f81074b182d1f4b4461be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= <cbosdonnat@suse.com>
Date: Thu, 4 Apr 2019 16:18:58 +0200
Subject: [PATCH] virt.volume_infos fix for single VM
_get_domain returns a domain object when only one VM has been found.
virt.volume_infos needs to take care of it or it will fail to list the
volumes informations if the host in such a case.
---
salt/modules/virt.py | 4 ++-
tests/unit/modules/test_virt.py | 46 +++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index 17039444c4..a3f625909d 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -5047,10 +5047,12 @@ def volume_infos(pool=None, volume=None, **kwargs):
conn = __get_conn(**kwargs)
try:
backing_stores = _get_all_volumes_paths(conn)
+ domains = _get_domain(conn)
+ domains_list = domains if isinstance(domains, list) else [domains]
disks = {domain.name():
{node.get('file') for node
in ElementTree.fromstring(domain.XMLDesc(0)).findall('.//disk/source/[@file]')}
- for domain in _get_domain(conn)}
+ for domain in domains_list}
def _volume_extract_infos(vol):
'''
diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py
index 14e51e1e2a..bbe8d813d7 100644
--- a/tests/unit/modules/test_virt.py
+++ b/tests/unit/modules/test_virt.py
@@ -2864,6 +2864,52 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
}
})
+ # Single VM test
+ with patch('salt.modules.virt._get_domain', MagicMock(return_value=mock_vms[0])):
+ actual = virt.volume_infos('pool0', 'vol0')
+ self.assertEqual(1, len(actual.keys()))
+ self.assertEqual(1, len(actual['pool0'].keys()))
+ self.assertEqual(['vm0'], sorted(actual['pool0']['vol0']['used_by']))
+ self.assertEqual('/path/to/vol0.qcow2', actual['pool0']['vol0']['path'])
+ self.assertEqual('file', actual['pool0']['vol0']['type'])
+ self.assertEqual('/key/of/vol0', actual['pool0']['vol0']['key'])
+ self.assertEqual(123456789, actual['pool0']['vol0']['capacity'])
+ self.assertEqual(123456, actual['pool0']['vol0']['allocation'])
+
+ self.assertEqual(virt.volume_infos('pool1', None), {
+ 'pool1': {
+ 'vol1': {
+ 'type': 'file',
+ 'key': '/key/of/vol1',
+ 'path': '/path/to/vol1.qcow2',
+ 'capacity': 12345,
+ 'allocation': 1234,
+ 'used_by': [],
+ },
+ 'vol2': {
+ 'type': 'file',
+ 'key': '/key/of/vol2',
+ 'path': '/path/to/vol2.qcow2',
+ 'capacity': 12345,
+ 'allocation': 1234,
+ 'used_by': [],
+ }
+ }
+ })
+
+ self.assertEqual(virt.volume_infos(None, 'vol2'), {
+ 'pool1': {
+ 'vol2': {
+ 'type': 'file',
+ 'key': '/key/of/vol2',
+ 'path': '/path/to/vol2.qcow2',
+ 'capacity': 12345,
+ 'allocation': 1234,
+ 'used_by': [],
+ }
+ }
+ })
+
def test_volume_delete(self):
'''
Test virt.volume_delete
--
2.21.0