mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #39162 from meaksh/snapper-module-improvements
Adding more function to Snapper module
This commit is contained in:
commit
b240468525
2 changed files with 201 additions and 8 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue