SHA256
1
0
forked from pool/salt
salt/virt.pool_running-fix-pool-start.patch

583 lines
31 KiB
Diff

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