Skip to content

Commit

Permalink
Allow Proxmox Snapshot Restoring (#4377)
Browse files Browse the repository at this point in the history
* Allow restoring of snapshots

* Fix formatting

* Add documentation for new feature

* Revert unrelated reformatting

* Add documentation for snapshot change

* Remove redundant multiple call to status API

* Remove unneccesary indent

* Add documentation for timeout fix

* Update changelog fragment to reflect real changes

Co-authored-by: Felix Fontein <[email protected]>

* Update changelog fragment to reflect real changes

Co-authored-by: Felix Fontein <[email protected]>

* Add Tests for Snapshot rollback

* Update tests/unit/plugins/modules/cloud/misc/test_proxmox_snap.py

Co-authored-by: Felix Fontein <[email protected]>

* Update changelogs/fragments/4377-allow-proxmox-snapshot-restoring.yml

Co-authored-by: Felix Fontein <[email protected]>

* Update plugins/modules/cloud/misc/proxmox_snap.py

Co-authored-by: Felix Fontein <[email protected]>

Co-authored-by: Felix Fontein <[email protected]>
(cherry picked from commit dbad1e0)
  • Loading branch information
EinDev authored and patchback[bot] committed Apr 25, 2022
1 parent 759e82d commit e7cd9f5
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- proxmox_snap - add restore snapshot option (https://github.com/ansible-collections/community.general/pull/4377).
- proxmox_snap - fixed timeout value to correctly reflect time in seconds. The timeout was off by one second (https://github.com/ansible-collections/community.general/pull/4377).
66 changes: 56 additions & 10 deletions plugins/modules/cloud/misc/proxmox_snap.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
short_description: Snapshot management of instances in Proxmox VE cluster
version_added: 2.0.0
description:
- Allows you to create/delete snapshots from instances in Proxmox VE cluster.
- Allows you to create/delete/restore snapshots from instances in Proxmox VE cluster.
- Supports both KVM and LXC, OpenVZ has not been tested, as it is no longer supported on Proxmox VE.
options:
hostname:
Expand All @@ -28,7 +28,8 @@
state:
description:
- Indicate desired state of the instance snapshot.
choices: ['present', 'absent']
- The C(rollback) value was added in community.general 4.8.0.
choices: ['present', 'absent', 'rollback']
default: present
type: str
force:
Expand All @@ -53,7 +54,7 @@
type: int
snapname:
description:
- Name of the snapshot that has to be created.
- Name of the snapshot that has to be created/deleted/restored.
default: 'ansible_snap'
type: str
Expand Down Expand Up @@ -84,6 +85,15 @@
vmid: 100
state: absent
snapname: pre-updates
- name: Rollback container snapshot
community.general.proxmox_snap:
api_user: root@pam
api_password: 1q2w3e
api_host: node1
vmid: 100
state: rollback
snapname: pre-updates
'''

RETURN = r'''#'''
Expand All @@ -109,15 +119,15 @@ def snapshot_create(self, vm, vmid, timeout, snapname, description, vmstate):
else:
taskid = self.snapshot(vm, vmid).post(snapname=snapname, description=description, vmstate=int(vmstate))
while timeout:
if (self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()['status'] == 'stopped' and
self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()['exitstatus'] == 'OK'):
status_data = self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()
if status_data['status'] == 'stopped' and status_data['exitstatus'] == 'OK':
return True
timeout -= 1
if timeout == 0:
self.module.fail_json(msg='Reached timeout while waiting for creating VM snapshot. Last line in task before timeout: %s' %
self.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1])

time.sleep(1)
timeout -= 1
return False

def snapshot_remove(self, vm, vmid, timeout, snapname, force):
Expand All @@ -126,15 +136,32 @@ def snapshot_remove(self, vm, vmid, timeout, snapname, force):

taskid = self.snapshot(vm, vmid).delete(snapname, force=int(force))
while timeout:
if (self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()['status'] == 'stopped' and
self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()['exitstatus'] == 'OK'):
status_data = self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()
if status_data['status'] == 'stopped' and status_data['exitstatus'] == 'OK':
return True
timeout -= 1
if timeout == 0:
self.module.fail_json(msg='Reached timeout while waiting for removing VM snapshot. Last line in task before timeout: %s' %
self.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1])

time.sleep(1)
timeout -= 1
return False

def snapshot_rollback(self, vm, vmid, timeout, snapname):
if self.module.check_mode:
return True

taskid = self.snapshot(vm, vmid)(snapname).post("rollback")
while timeout:
status_data = self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()
if status_data['status'] == 'stopped' and status_data['exitstatus'] == 'OK':
return True
if timeout == 0:
self.module.fail_json(msg='Reached timeout while waiting for rolling back VM snapshot. Last line in task before timeout: %s' %
self.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1])

time.sleep(1)
timeout -= 1
return False


Expand All @@ -144,7 +171,7 @@ def main():
vmid=dict(required=False),
hostname=dict(),
timeout=dict(type='int', default=30),
state=dict(default='present', choices=['present', 'absent']),
state=dict(default='present', choices=['present', 'absent', 'rollback']),
description=dict(type='str'),
snapname=dict(type='str', default='ansible_snap'),
force=dict(type='bool', default='no'),
Expand Down Expand Up @@ -211,6 +238,25 @@ def main():

except Exception as e:
module.fail_json(msg="Removing snapshot %s of VM %s failed with exception: %s" % (snapname, vmid, to_native(e)))
elif state == 'rollback':
try:
snap_exist = False

for i in proxmox.snapshot(vm, vmid).get():
if i['name'] == snapname:
snap_exist = True
continue

if not snap_exist:
module.exit_json(changed=False, msg="Snapshot %s does not exist" % snapname)
if proxmox.snapshot_rollback(vm, vmid, timeout, snapname):
if module.check_mode:
module.exit_json(changed=True, msg="Snapshot %s would be rolled back" % snapname)
else:
module.exit_json(changed=True, msg="Snapshot %s rolled back" % snapname)

except Exception as e:
module.fail_json(msg="Rollback of snapshot %s of VM %s failed with exception: %s" % (snapname, vmid, to_native(e)))


if __name__ == '__main__':
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/plugins/modules/cloud/misc/test_proxmox_snap.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,26 @@ def test_remove_snapshot_check_mode(connect_mock, capfd, mocker):
out, err = capfd.readouterr()
assert not err
assert not json.loads(out)['changed']


@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect')
def test_rollback_snapshot_check_mode(connect_mock, capfd, mocker):
set_module_args({"hostname": "test-lxc",
"api_user": "root@pam",
"api_password": "secret",
"api_host": "127.0.0.1",
"state": "rollback",
"snapname": "test",
"timeout": "1",
"force": True,
"_ansible_check_mode": True})
proxmox_utils.HAS_PROXMOXER = True
connect_mock.side_effect = lambda: fake_api(mocker)
with pytest.raises(SystemExit) as results:
proxmox_snap.main()

out, err = capfd.readouterr()
assert not err
output = json.loads(out)
assert not output['changed']
assert output['msg'] == "Snapshot test does not exist"

0 comments on commit e7cd9f5

Please sign in to comment.