Add snapshot and revert to libvirt driver

This patch allows to create and revert snapshots
using libvirt driver.

Change-Id: Id6bc0a3daf14f5e664d068bc8d2348e48e561a9b
This commit is contained in:
Anton Studenov 2016-11-18 15:19:42 +03:00
parent 01801c3ecd
commit f92fd9ca8b
9 changed files with 165 additions and 16 deletions

View File

@ -165,6 +165,20 @@ class NodeCollection(object):
LOG.info('Reset nodes: %s', self)
self.power_management.reset(self.get_macs())
def snapshot(self, snapshot_name, suspend=True):
"""Create snapshot for all nodes
"""
LOG.info('Create snapshot "%s" for nodes: %s', snapshot_name, self)
self.power_management.snapshot(self.get_macs(), snapshot_name, suspend)
def revert(self, snapshot_name, resume=True):
"""Revert snapshot for all nodes
"""
LOG.info('Revert snapshot "%s" for nodes: %s', snapshot_name, self)
self.power_management.revert(self.get_macs(), snapshot_name, resume)
@public
def disconnect(self, network_name):
"""Disconnect nodes from <network_name> network

View File

@ -32,3 +32,9 @@ class PowerManagement(base_driver.BaseDriver):
@abc.abstractmethod
def reset(self, hosts):
pass
def snapshot(self, hosts, snapshot_name, suspend=True):
raise NotImplementedError
def revert(self, hosts, snapshot_name, resume=True):
raise NotImplementedError

View File

@ -100,10 +100,16 @@ class IPMIDriver(power_management.PowerManagement):
LOG.info('Node reset: %s', mac_address)
def poweroff(self, mac_addresses_list):
utils.run(self._poweroff, mac_addresses_list)
kwargs_list = [dict(mac_address=mac_address)
for mac_address in mac_addresses_list]
utils.run(self._poweroff, kwargs_list)
def poweron(self, mac_addresses_list):
utils.run(self._poweron, mac_addresses_list)
kwargs_list = [dict(mac_address=mac_address)
for mac_address in mac_addresses_list]
utils.run(self._poweron, kwargs_list)
def reset(self, mac_addresses_list):
utils.run(self._reset, mac_addresses_list)
kwargs_list = [dict(mac_address=mac_address)
for mac_address in mac_addresses_list]
utils.run(self._reset, kwargs_list)

View File

@ -28,7 +28,6 @@ class LibvirtDriver(power_management.PowerManagement):
'$schema': 'http://json-schema.org/draft-04/schema#',
'properties': {
'connection_uri': {'type': 'string'},
},
'required': ['connection_uri'],
'additionalProperties': False,
@ -79,11 +78,56 @@ class LibvirtDriver(power_management.PowerManagement):
domain.reset()
LOG.info('Domain reset: %s', mac_address)
def _snapshot(self, mac_address, snapshot_name, suspend):
LOG.debug('Create snapshot "%s" for domain with MAC address: %s',
snapshot_name, mac_address)
domain = self._find_domain_by_mac_address(mac_address)
if suspend:
domain.suspend()
domain.snapshotCreateXML(
'<domainsnapshot><name>{}</name></domainsnapshot>'.format(
snapshot_name))
if suspend:
domain.resume()
LOG.debug('Created snapshot "%s" for domain with MAC address: %s',
snapshot_name, mac_address)
def _revert(self, mac_address, snapshot_name, resume):
LOG.debug('Revert snapshot "%s" for domain with MAC address: %s',
snapshot_name, mac_address)
domain = self._find_domain_by_mac_address(mac_address)
snapshot = domain.snapshotLookupByName(snapshot_name)
domain.revertToSnapshot(snapshot)
if resume:
domain.resume()
LOG.debug('Reverted snapshot "%s" for domain with MAC address: %s',
snapshot_name, mac_address)
def poweroff(self, mac_addresses_list):
utils.run(self._poweroff, mac_addresses_list)
kwargs_list = [dict(mac_address=mac_address)
for mac_address in mac_addresses_list]
utils.run(self._poweroff, kwargs_list)
def poweron(self, mac_addresses_list):
utils.run(self._poweron, mac_addresses_list)
kwargs_list = [dict(mac_address=mac_address)
for mac_address in mac_addresses_list]
utils.run(self._poweron, kwargs_list)
def reset(self, mac_addresses_list):
utils.run(self._reset, mac_addresses_list)
kwargs_list = [dict(mac_address=mac_address)
for mac_address in mac_addresses_list]
utils.run(self._reset, kwargs_list)
def snapshot(self, mac_addresses_list, snapshot_name, suspend=True):
kwargs_list = [dict(mac_address=mac_address,
snapshot_name=snapshot_name,
suspend=suspend)
for mac_address in mac_addresses_list]
utils.run(self._snapshot, kwargs_list)
def revert(self, mac_addresses_list, snapshot_name, resume=True):
kwargs_list = [dict(mac_address=mac_address,
snapshot_name=snapshot_name,
resume=resume)
for mac_address in mac_addresses_list]
utils.run(self._revert, kwargs_list)

View File

@ -198,3 +198,15 @@ class NodeCollectionTestCase(test.TestCase):
self.mock_cloud_management.execute_on_cloud.assert_called_once_with(
['10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5'],
{'command': 'reboot now'})
def test_snapshot(self):
self.node_collection.snapshot('foo')
self.mock_power_management.snapshot.assert_called_once_with(
['09:7b:74:90:63:c1', '09:7b:74:90:63:c2',
'09:7b:74:90:63:c3', '09:7b:74:90:63:c4'], 'foo', True)
def test_revert(self):
self.node_collection.revert('foo')
self.mock_power_management.revert.assert_called_once_with(
['09:7b:74:90:63:c1', '09:7b:74:90:63:c2',
'09:7b:74:90:63:c3', '09:7b:74:90:63:c4'], 'foo', True)

View File

@ -89,5 +89,7 @@ class IPMIDriverTestCase(test.TestCase):
def test_driver_actions(self, action, mock_run):
macs_list = ['00:00:00:00:00:00', '00:00:00:00:00:01']
getattr(self.driver, action)(macs_list)
mock_run.assert_called_once_with(getattr(self.driver, '_%s' % action),
macs_list)
mock_run.assert_called_once_with(
getattr(self.driver, '_%s' % action),
[{'mac_address': '00:00:00:00:00:00'},
{'mac_address': '00:00:00:00:00:01'}])

View File

@ -77,10 +77,74 @@ class LibvirtDriverTestCase(test.TestCase):
domain = mock__find_domain_by_mac_address.return_value
getattr(domain, actions[1]).assert_called_once_with()
@mock.patch(DRIVER_PATH + '.LibvirtDriver._find_domain_by_mac_address')
def test__snapshot(self, mock__find_domain_by_mac_address):
self.driver._snapshot('52:54:00:f9:b8:f9', 'foo', suspend=False)
domain = mock__find_domain_by_mac_address.return_value
domain.snapshotCreateXML.assert_called_once_with(
'<domainsnapshot><name>foo</name></domainsnapshot>')
@mock.patch(DRIVER_PATH + '.LibvirtDriver._find_domain_by_mac_address')
def test__snapshot_suspend(self, mock__find_domain_by_mac_address):
self.driver._snapshot('52:54:00:f9:b8:f9', 'foo', suspend=True)
domain = mock__find_domain_by_mac_address.return_value
domain.assert_has_calls((
mock.call.suspend(),
mock.call.snapshotCreateXML(
'<domainsnapshot><name>foo</name></domainsnapshot>'),
mock.call.resume(),
))
@mock.patch(DRIVER_PATH + '.LibvirtDriver._find_domain_by_mac_address')
def test__revert(self, mock__find_domain_by_mac_address):
self.driver._revert('52:54:00:f9:b8:f9', 'foo', resume=False)
domain = mock__find_domain_by_mac_address.return_value
snapshot = domain.snapshotLookupByName.return_value
domain.snapshotLookupByName.assert_called_once_with('foo')
domain.revertToSnapshot.assert_called_once_with(snapshot)
self.assertFalse(domain.resume.called)
@mock.patch(DRIVER_PATH + '.LibvirtDriver._find_domain_by_mac_address')
def test__revert_resume(self, mock__find_domain_by_mac_address):
self.driver._revert('52:54:00:f9:b8:f9', 'foo', resume=True)
domain = mock__find_domain_by_mac_address.return_value
snapshot = domain.snapshotLookupByName.return_value
domain.snapshotLookupByName.assert_called_once_with('foo')
domain.revertToSnapshot.assert_called_once_with(snapshot)
domain.resume.assert_called_once_with()
@mock.patch('os_faults.utils.run')
@ddt.data('poweroff', 'poweron', 'reset')
def test_driver_actions(self, action, mock_run):
macs_list = ['52:54:00:f9:b8:f9', '52:54:00:ab:64:42']
getattr(self.driver, action)(macs_list)
mock_run.assert_called_once_with(getattr(self.driver, '_%s' % action),
macs_list)
mock_run.assert_called_once_with(
getattr(self.driver, '_%s' % action),
[{'mac_address': '52:54:00:f9:b8:f9'},
{'mac_address': '52:54:00:ab:64:42'}])
@mock.patch('os_faults.utils.run')
def test_driver_snapshot(self, mock_run):
macs_list = ['52:54:00:f9:b8:f9', '52:54:00:ab:64:42']
self.driver.snapshot(macs_list, 'foo_snap')
mock_run.assert_called_once_with(
self.driver._snapshot,
[{'mac_address': '52:54:00:f9:b8:f9',
'snapshot_name': 'foo_snap',
'suspend': True},
{'mac_address': '52:54:00:ab:64:42',
'snapshot_name': 'foo_snap',
'suspend': True}])
@mock.patch('os_faults.utils.run')
def test_driver_revert(self, mock_run):
macs_list = ['52:54:00:f9:b8:f9', '52:54:00:ab:64:42']
self.driver.revert(macs_list, 'foo_snap', resume=False)
mock_run.assert_called_once_with(
self.driver._revert,
[{'mac_address': '52:54:00:f9:b8:f9',
'snapshot_name': 'foo_snap',
'resume': False},
{'mac_address': '52:54:00:ab:64:42',
'snapshot_name': 'foo_snap',
'resume': False}])

View File

@ -27,7 +27,7 @@ class UtilsTestCase(test.TestCase):
def test_run(self):
target = mock.Mock()
utils.run(target, ['01', '02'])
utils.run(target, [{'mac_address': '01'}, {'mac_address': '02'}])
target.assert_has_calls([mock.call(mac_address='01'),
mock.call(mac_address='02')])
@ -35,7 +35,8 @@ class UtilsTestCase(test.TestCase):
target = mock.Mock()
target.side_effect = MyException()
self.assertRaises(error.PowerManagementError,
utils.run, target, ['01', '02'])
utils.run, target, [{'mac_address': '01'},
{'mac_address': '02'}])
def test_start_thread(self):
target = mock.Mock()

View File

@ -20,10 +20,10 @@ from os_faults.api import error
LOG = logging.getLogger(__name__)
def run(target, mac_addresses_list):
def run(target, kwargs_list):
tw = ThreadsWrapper(target)
for mac_address in mac_addresses_list:
tw.start_thread(mac_address=mac_address)
for kwargs in kwargs_list:
tw.start_thread(**kwargs)
tw.join_threads()
if tw.errors: