parent
174e6e08e3
commit
33f905459a
|
@ -3202,6 +3202,54 @@ class API_VM_Snapshot(Resource):
|
|||
api.add_resource(API_VM_Snapshot, "/vm/<vm>/snapshot")
|
||||
|
||||
|
||||
# /vm/<vm>/snapshot/rollback
|
||||
class API_VM_Snapshot_Rollback(Resource):
|
||||
@RequestParser(
|
||||
[
|
||||
{
|
||||
"name": "snapshot_name",
|
||||
"required": True,
|
||||
"helptext": "A snapshot name must be specified",
|
||||
},
|
||||
]
|
||||
)
|
||||
@Authenticator
|
||||
def post(self, vm, reqargs):
|
||||
"""
|
||||
Roll back to a snapshot of a VM's disks and configuration
|
||||
---
|
||||
tags:
|
||||
- vm
|
||||
parameters:
|
||||
- in: query
|
||||
name: snapshot_name
|
||||
type: string
|
||||
required: true
|
||||
description: The name of the snapshot to roll back to
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
schema:
|
||||
type: object
|
||||
id: Message
|
||||
400:
|
||||
description: Execution error
|
||||
schema:
|
||||
type: object
|
||||
id: Message
|
||||
404:
|
||||
description: Not found
|
||||
schema:
|
||||
type: object
|
||||
id: Message
|
||||
"""
|
||||
snapshot_name = reqargs.get("snapshot_name", None)
|
||||
return api_helper.rollback_vm_snapshot(vm, snapshot_name)
|
||||
|
||||
|
||||
api.add_resource(API_VM_Snapshot_Rollback, "/vm/<vm>/snapshot/rollback")
|
||||
|
||||
|
||||
##########################################################
|
||||
# Client API - Network
|
||||
##########################################################
|
||||
|
|
|
@ -813,6 +813,30 @@ def remove_vm_snapshot(
|
|||
return output, retcode
|
||||
|
||||
|
||||
@ZKConnection(config)
|
||||
def rollback_vm_snapshot(
|
||||
zkhandler,
|
||||
domain,
|
||||
snapshot_name,
|
||||
):
|
||||
"""
|
||||
Roll back to a snapshot of a VM.
|
||||
"""
|
||||
retflag, retdata = pvc_vm.rollback_vm_snapshot(
|
||||
zkhandler,
|
||||
domain,
|
||||
snapshot_name,
|
||||
)
|
||||
|
||||
if retflag:
|
||||
retcode = 200
|
||||
else:
|
||||
retcode = 400
|
||||
|
||||
output = {"message": retdata.replace('"', "'")}
|
||||
return output, retcode
|
||||
|
||||
|
||||
@ZKConnection(config)
|
||||
def vm_attach_device(zkhandler, vm, device_spec_xml):
|
||||
"""
|
||||
|
|
|
@ -1787,7 +1787,7 @@ def cli_vm_snapshot():
|
|||
@connection_req
|
||||
@click.argument("domain")
|
||||
@click.argument("snapshot_name", required=False, default=None)
|
||||
def cli_vm_snapshot_remove(domain, snapshot_name):
|
||||
def cli_vm_snapshot_create(domain, snapshot_name):
|
||||
"""
|
||||
Create a snapshot of the disks and XML configuration of virtual machine DOMAIN, with the
|
||||
optional name SNAPSHOT_NAME. DOMAIN may be a UUID or name.
|
||||
|
@ -1819,7 +1819,7 @@ def cli_vm_snapshot_remove(domain, snapshot_name):
|
|||
@connection_req
|
||||
@click.argument("domain")
|
||||
@click.argument("snapshot_name")
|
||||
def cli_vm_snapshot_create(domain, snapshot_name):
|
||||
def cli_vm_snapshot_remove(domain, snapshot_name):
|
||||
"""
|
||||
Remove the snapshot SNAPSHOT_NAME of the disks and XML configuration of virtual machine DOMAIN,
|
||||
DOMAIN may be a UUID or name.
|
||||
|
@ -1838,6 +1838,37 @@ def cli_vm_snapshot_create(domain, snapshot_name):
|
|||
finish(retcode, retmsg)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# > pvc vm snapshot rollback
|
||||
###############################################################################
|
||||
@click.command(
|
||||
name="rollback", short_help="Roll back to a snapshot of a virtual machine."
|
||||
)
|
||||
@connection_req
|
||||
@click.argument("domain")
|
||||
@click.argument("snapshot_name")
|
||||
@confirm_opt(
|
||||
"Roll back to snapshot {snapshot_name} of {domain} and lose all data and changes since this snapshot"
|
||||
)
|
||||
def cli_vm_snapshot_rollback(domain, snapshot_name):
|
||||
"""
|
||||
Roll back to the snapshot SNAPSHOT_NAME of the disks and XML configuration of virtual machine DOMAIN,
|
||||
DOMAIN may be a UUID or name.
|
||||
"""
|
||||
|
||||
echo(
|
||||
CLI_CONFIG,
|
||||
f"Rolling back to snapshot '{snapshot_name}' of VM '{domain}'... ",
|
||||
newline=False,
|
||||
)
|
||||
retcode, retmsg = pvc.lib.vm.vm_rollback_snapshot(CLI_CONFIG, domain, snapshot_name)
|
||||
if retcode:
|
||||
echo(CLI_CONFIG, "done.")
|
||||
else:
|
||||
echo(CLI_CONFIG, "failed.")
|
||||
finish(retcode, retmsg)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# > pvc vm backup
|
||||
###############################################################################
|
||||
|
@ -6377,6 +6408,7 @@ cli_vm.add_command(cli_vm_unmigrate)
|
|||
cli_vm.add_command(cli_vm_flush_locks)
|
||||
cli_vm_snapshot.add_command(cli_vm_snapshot_create)
|
||||
cli_vm_snapshot.add_command(cli_vm_snapshot_remove)
|
||||
cli_vm_snapshot.add_command(cli_vm_snapshot_rollback)
|
||||
cli_vm.add_command(cli_vm_snapshot)
|
||||
cli_vm_backup.add_command(cli_vm_backup_create)
|
||||
cli_vm_backup.add_command(cli_vm_backup_restore)
|
||||
|
|
|
@ -538,6 +538,25 @@ def vm_remove_snapshot(config, vm, snapshot_name):
|
|||
return True, response.json().get("message", "")
|
||||
|
||||
|
||||
def vm_rollback_snapshot(config, vm, snapshot_name):
|
||||
"""
|
||||
Roll back to a snapshot of a VM's disks and configuration
|
||||
|
||||
API endpoint: POST /vm/{vm}/snapshot/rollback
|
||||
API arguments: snapshot_name=snapshot_name
|
||||
API schema: {"message":"{data}"}
|
||||
"""
|
||||
params = {"snapshot_name": snapshot_name}
|
||||
response = call_api(
|
||||
config, "post", "/vm/{vm}/snapshot/rollback".format(vm=vm), params=params
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
return False, response.json().get("message", "")
|
||||
else:
|
||||
return True, response.json().get("message", "")
|
||||
|
||||
|
||||
def vm_vcpus_set(config, vm, vcpus, topology, restart):
|
||||
"""
|
||||
Set the vCPU count of the VM with topology
|
||||
|
|
|
@ -1396,7 +1396,67 @@ def remove_vm_snapshot(zkhandler, domain, snapshot_name):
|
|||
|
||||
|
||||
def rollback_vm_snapshot(zkhandler, domain, snapshot_name):
|
||||
pass
|
||||
# Validate that VM exists in cluster
|
||||
dom_uuid = getDomainUUID(zkhandler, domain)
|
||||
if not dom_uuid:
|
||||
return False, 'ERROR: Could not find VM "{}" in the cluster!'.format(domain)
|
||||
|
||||
# Verify that the VM is in a stopped state; renaming is not supported otherwise
|
||||
state = zkhandler.read(("domain.state", dom_uuid))
|
||||
if state not in ["stop", "disable"]:
|
||||
return (
|
||||
False,
|
||||
'ERROR: VM "{}" is not in stopped state; VMs cannot be rolled back while running.'.format(
|
||||
domain
|
||||
),
|
||||
)
|
||||
|
||||
# Verify that the snapshot exists
|
||||
if not zkhandler.exists(
|
||||
("domain.snapshots", dom_uuid, "domain_snapshot.name", snapshot_name)
|
||||
):
|
||||
return (
|
||||
False,
|
||||
f'ERROR: Could not find snapshot "{snapshot_name}" of VM "{domain}"!',
|
||||
)
|
||||
|
||||
tstart = time.time()
|
||||
|
||||
_snapshots = zkhandler.read(
|
||||
("domain.snapshots", dom_uuid, "domain_snapshot.rbd_snapshots", snapshot_name)
|
||||
)
|
||||
rbd_snapshots = _snapshots.split(",")
|
||||
for snap in rbd_snapshots:
|
||||
rbd, name = snap.split("@")
|
||||
pool, volume = rbd.split("/")
|
||||
ret, msg = ceph.rollback_snapshot(zkhandler, pool, volume, name)
|
||||
if not ret:
|
||||
return False, msg
|
||||
|
||||
# Get the snapshot domain XML
|
||||
vm_config = zkhandler.read(
|
||||
("domain.snapshots", dom_uuid, "domain_snapshot.xml", snapshot_name)
|
||||
)
|
||||
|
||||
# Write the restored config to the main XML config
|
||||
zkhandler.write(
|
||||
[
|
||||
(
|
||||
(
|
||||
"domain.xml",
|
||||
dom_uuid,
|
||||
),
|
||||
vm_config,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
tend = time.time()
|
||||
ttot = round(tend - tstart, 2)
|
||||
return (
|
||||
True,
|
||||
f'Successfully rolled back to snapshot "{snapshot_name}" of VM "{domain}" in {ttot}s.',
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
|
|
Loading…
Reference in New Issue