Merge pull request #39162 from meaksh/snapper-module-improvements

Adding more function to Snapper module
This commit is contained in:
Mike Place 2017-02-06 11:33:52 -07:00 committed by GitHub
commit b240468525
2 changed files with 201 additions and 8 deletions

View file

@ -290,6 +290,60 @@ def get_config(name='root'):
)
def create_config(name=None,
subvolume=None,
fstype=None,
template=None,
extra_opts=None):
'''
Creates a new Snapper configuration
name
Name of the new Snapper configuration.
subvolume
Path to the related subvolume.
fstype
Filesystem type of the subvolume.
template
Configuration template to use. (Default: default)
extra_opts
Extra Snapper configuration opts dictionary. It will override the values provided
by the given template (if any).
CLI example:
.. code-block:: bash
salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs
salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs template="default"
salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs extra_opts='{"NUMBER_CLEANUP": False}'
'''
def raise_arg_error(argname):
raise CommandExecutionError(
'You must provide a "{0}" for the new configuration'.format(argname)
)
if not name:
raise_arg_error("name")
if not subvolume:
raise_arg_error("subvolume")
if not fstype:
raise_arg_error("fstype")
if not template:
template = ""
try:
snapper.CreateConfig(name, subvolume, fstype, template)
if extra_opts:
set_config(name, **extra_opts)
return get_config(name)
except dbus.DBusException as exc:
raise CommandExecutionError(
'Error encountered while creating the new configuration: {0}'
.format(_dbus_exception_to_reason(exc, locals()))
)
def create_snapshot(config='root', snapshot_type='single', pre_number=None,
description=None, cleanup_algorithm='number', userdata=None,
**kwargs):
@ -309,14 +363,14 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None,
cleanup_algorithm
Set the cleanup algorithm for the snapshot.
number
Deletes old snapshots when a certain number of snapshots
is reached.
timeline
Deletes old snapshots but keeps a number of hourly,
daily, weekly, monthly and yearly snapshots.
empty-pre-post
Deletes pre/post snapshot pairs with empty diffs.
number
Deletes old snapshots when a certain number of snapshots
is reached.
timeline
Deletes old snapshots but keeps a number of hourly,
daily, weekly, monthly and yearly snapshots.
empty-pre-post
Deletes pre/post snapshot pairs with empty diffs.
userdata
Set userdata for the snapshot (key-value pairs).
@ -364,6 +418,95 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None,
return new_nr
def delete_snapshot(snapshots_ids=None, config="root"):
'''
Deletes an snapshot
config
Configuration name. (Default: root)
snapshots_ids
List of the snapshots IDs to be deleted.
CLI example:
.. code-block:: bash
salt '*' snapper.delete_snapshot 54
salt '*' snapper.delete_snapshot config=root 54
salt '*' snapper.delete_snapshot config=root snapshots_ids=[54,55,56]
'''
if not snapshots_ids:
raise CommandExecutionError('Error: No snapshot ID has been provided')
try:
current_snapshots_ids = [x['id'] for x in list_snapshots(config)]
if not isinstance(snapshots_ids, list):
snapshots_ids = [snapshots_ids]
if not set(snapshots_ids).issubset(set(current_snapshots_ids)):
raise CommandExecutionError(
"Error: Snapshots '{0}' not found".format(", ".join(
[str(x) for x in set(snapshots_ids).difference(
set(current_snapshots_ids))]))
)
snapper.DeleteSnapshots(config, snapshots_ids)
return {config: {"ids": snapshots_ids, "status": "deleted"}}
except dbus.DBusException as exc:
raise CommandExecutionError(_dbus_exception_to_reason(exc, locals()))
def modify_snapshot(snapshot_id=None,
description=None,
userdata=None,
cleanup=None,
config="root"):
'''
Modify attributes of an existing snapshot.
config
Configuration name. (Default: root)
snapshot_id
ID of the snapshot to be modified.
cleanup
Change the cleanup method of the snapshot. (str)
description
Change the description of the snapshot. (str)
userdata
Change the userdata dictionary of the snapshot. (dict)
CLI example:
.. code-block:: bash
salt '*' snapper.modify_snapshot 54 description="my snapshot description"
salt '*' snapper.modify_snapshot 54 description="my snapshot description"
salt '*' snapper.modify_snapshot 54 userdata='{"foo": "bar"}'
salt '*' snapper.modify_snapshot snapshot_id=54 cleanup="number"
'''
if not snapshot_id:
raise CommandExecutionError('Error: No snapshot ID has been provided')
snapshot = get_snapshot(config=config, number=snapshot_id)
try:
# Updating only the explicitely provided attributes by the user
updated_opts = {
'description': description if description is not None else snapshot['description'],
'cleanup': cleanup if cleanup is not None else snapshot['cleanup'],
'userdata': userdata if userdata is not None else snapshot['userdata'],
}
snapper.SetSnapshot(config,
snapshot_id,
updated_opts['description'],
updated_opts['cleanup'],
updated_opts['userdata'])
return get_snapshot(config=config, number=snapshot_id)
except dbus.DBusException as exc:
raise CommandExecutionError(_dbus_exception_to_reason(exc, locals()))
def _get_num_interval(config, num_pre, num_post):
'''
Returns numerical interval based on optionals num_pre, num_post values

View file

@ -202,6 +202,26 @@ class SnapperTestCase(TestCase):
self.assertEqual(snapper.status_to_string(128), ["extended attributes changed"])
self.assertEqual(snapper.status_to_string(256), ["ACL info changed"])
@patch('salt.modules.snapper.snapper.CreateConfig', MagicMock())
@patch('salt.modules.snapper.snapper.GetConfig', MagicMock(return_value=DBUS_RET['ListConfigs'][0]))
def test_create_config(self):
opts = {
'name': 'testconfig',
'subvolume': '/foo/bar/',
'fstype': 'btrfs',
'template': 'mytemplate',
'extra_opts': {"NUMBER_CLEANUP": False},
}
with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock:
self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0])
set_config_mock.assert_called_with("testconfig", **opts['extra_opts'])
with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock:
del opts['extra_opts']
self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0])
assert not set_config_mock.called
self.assertRaises(CommandExecutionError, snapper.create_config)
@patch('salt.modules.snapper.snapper.CreateSingleSnapshot', MagicMock(return_value=1234))
@patch('salt.modules.snapper.snapper.CreatePreSnapshot', MagicMock(return_value=1234))
@patch('salt.modules.snapper.snapper.CreatePostSnapshot', MagicMock(return_value=1234))
@ -216,6 +236,36 @@ class SnapperTestCase(TestCase):
}
self.assertEqual(snapper.create_snapshot(**opts), 1234)
@patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock())
@patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots']))
def test_delete_snapshot_id_success(self):
self.assertEqual(snapper.delete_snapshot(snapshots_ids=43), {"root": {"ids": [43], "status": "deleted"}})
self.assertEqual(snapper.delete_snapshot(snapshots_ids=[42, 43]), {"root": {"ids": [42, 43], "status": "deleted"}})
@patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock())
@patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots']))
def test_delete_snapshot_id_fail(self):
self.assertRaises(CommandExecutionError, snapper.delete_snapshot)
self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1)
self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2])
@patch('salt.modules.snapper.snapper.SetSnapshot', MagicMock())
def test_modify_snapshot(self):
_ret = {
'userdata': {'userdata2': 'uservalue2'},
'description': 'UPDATED DESCRIPTION', 'timestamp': 1457006571,
'cleanup': 'number', 'user': 'root', 'type': 'pre', 'id': 42
}
_opts = {
'config': 'root',
'snapshot_id': 42,
'cleanup': 'number',
'description': 'UPDATED DESCRIPTION',
'userdata': {'userdata2': 'uservalue2'},
}
with patch('salt.modules.snapper.get_snapshot', MagicMock(side_effect=[DBUS_RET['ListSnapshots'][0], _ret])):
self.assertDictEqual(snapper.modify_snapshot(**_opts), _ret)
@patch('salt.modules.snapper._get_last_snapshot', MagicMock(return_value={'id': 42}))
def test__get_num_interval(self):
self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=None), (42, 0)) # pylint: disable=protected-access