Add support for rolling back snapshots
We supported creating snapshots, but not doing anything with them. This removes the manual task of restoring a snapshot and replace it with a PVC abstraction of rolling back to a snapshot. While Ceph recommends cloning a snapshot instead of rolling back, due to the time taken, in our usecase I don't think that is an optimal strategy, as it will leave dangling clones that we'd then have to manage. Closes #183
This commit is contained in:
parent
dcb9c0d12c
commit
5d0e7931d1
|
@ -6362,6 +6362,59 @@ api.add_resource(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# /storage/ceph/snapshot/<pool>/<volume>/<snapshot>/rollback
|
||||||
|
class API_Storage_Ceph_Snapshot_Rollback_Element(Resource):
|
||||||
|
@Authenticator
|
||||||
|
def post(self, pool, volume, snapshot):
|
||||||
|
"""
|
||||||
|
Roll back an RBD volume {volume} in pool {pool} to snapshot {snapshot}
|
||||||
|
|
||||||
|
WARNING: This action cannot be done on an active RBD volume. All IO MUST be stopped first.
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- storage / ceph
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: snapshot
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: The name of the snapshot
|
||||||
|
- in: query
|
||||||
|
name: volume
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: The name of the volume
|
||||||
|
- in: query
|
||||||
|
name: pool
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
description: The name of the pool
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
id: Message
|
||||||
|
404:
|
||||||
|
description: Not found
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
id: Message
|
||||||
|
400:
|
||||||
|
description: Bad request
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
id: Message
|
||||||
|
"""
|
||||||
|
return api_helper.ceph_volume_snapshot_rollback(pool, volume, snapshot)
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(
|
||||||
|
API_Storage_Ceph_Snapshot_Rollback_Element,
|
||||||
|
"/storage/ceph/snapshot/<pool>/<volume>/<snapshot>/rollback",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
##########################################################
|
##########################################################
|
||||||
# Provisioner API
|
# Provisioner API
|
||||||
##########################################################
|
##########################################################
|
||||||
|
|
|
@ -2183,6 +2183,22 @@ def ceph_volume_snapshot_rename(zkhandler, pool, volume, name, new_name):
|
||||||
return output, retcode
|
return output, retcode
|
||||||
|
|
||||||
|
|
||||||
|
@ZKConnection(config)
|
||||||
|
def ceph_volume_snapshot_rollback(zkhandler, pool, volume, name):
|
||||||
|
"""
|
||||||
|
Roll back a Ceph RBD volume to a given snapshot in the PVC Ceph storage cluster.
|
||||||
|
"""
|
||||||
|
retflag, retdata = pvc_ceph.rollback_snapshot(zkhandler, pool, volume, name)
|
||||||
|
|
||||||
|
if retflag:
|
||||||
|
retcode = 200
|
||||||
|
else:
|
||||||
|
retcode = 400
|
||||||
|
|
||||||
|
output = {"message": retdata.replace('"', "'")}
|
||||||
|
return output, retcode
|
||||||
|
|
||||||
|
|
||||||
@ZKConnection(config)
|
@ZKConnection(config)
|
||||||
def ceph_volume_snapshot_remove(zkhandler, pool, volume, name):
|
def ceph_volume_snapshot_remove(zkhandler, pool, volume, name):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -4372,6 +4372,32 @@ def cli_storage_volume_snapshot_remove(pool, volume, name):
|
||||||
finish(retcode, retmsg)
|
finish(retcode, retmsg)
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# > pvc storage volume snapshot rollback
|
||||||
|
###############################################################################
|
||||||
|
@click.command(name="rollback", short_help="Roll back RBD volume to snapshot.")
|
||||||
|
@connection_req
|
||||||
|
@click.argument("pool")
|
||||||
|
@click.argument("volume")
|
||||||
|
@click.argument("name")
|
||||||
|
@confirm_opt("Roll back to snapshot {name} for volume {pool}/{volume}")
|
||||||
|
def cli_storage_volume_snapshot_rollback(pool, volume, name):
|
||||||
|
"""
|
||||||
|
Roll back the Ceph RBD volume VOLUME in pool POOL to the snapshot NAME.
|
||||||
|
|
||||||
|
DANGER: All data written to the volume since the given snapshot will be permanently lost.
|
||||||
|
|
||||||
|
WARNING: A rollback cannot be performed on an RBD volume with active I/O. Doing so will cause
|
||||||
|
undefined behaviour and possible corruption. Ensure that any VM(s) using this RBD volume are
|
||||||
|
stopped or disabled before attempting a snapshot rollback.
|
||||||
|
"""
|
||||||
|
|
||||||
|
retcode, retmsg = pvc.lib.storage.ceph_snapshot_rollback(
|
||||||
|
CLI_CONFIG, pool, volume, name
|
||||||
|
)
|
||||||
|
finish(retcode, retmsg)
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# > pvc storage volume snapshot list
|
# > pvc storage volume snapshot list
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -6349,6 +6375,7 @@ cli_storage_volume.add_command(cli_storage_volume_list)
|
||||||
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_add)
|
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_add)
|
||||||
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_rename)
|
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_rename)
|
||||||
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_remove)
|
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_remove)
|
||||||
|
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_rollback)
|
||||||
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_list)
|
cli_storage_volume_snapshot.add_command(cli_storage_volume_snapshot_list)
|
||||||
cli_storage_volume.add_command(cli_storage_volume_snapshot)
|
cli_storage_volume.add_command(cli_storage_volume_snapshot)
|
||||||
cli_storage.add_command(cli_storage_volume)
|
cli_storage.add_command(cli_storage_volume)
|
||||||
|
|
|
@ -1544,6 +1544,30 @@ def ceph_snapshot_add(config, pool, volume, snapshot):
|
||||||
return retstatus, response.json().get("message", "")
|
return retstatus, response.json().get("message", "")
|
||||||
|
|
||||||
|
|
||||||
|
def ceph_snapshot_rollback(config, pool, volume, snapshot):
|
||||||
|
"""
|
||||||
|
Roll back Ceph volume to snapshot
|
||||||
|
|
||||||
|
API endpoint: POST /api/v1/storage/ceph/snapshot/{pool}/{volume}/{snapshot}/rollback
|
||||||
|
API arguments:
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
response = call_api(
|
||||||
|
config,
|
||||||
|
"post",
|
||||||
|
"/storage/ceph/snapshot/{pool}/{volume}/{snapshot}/rollback".format(
|
||||||
|
snapshot=snapshot, volume=volume, pool=pool
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json().get("message", "")
|
||||||
|
|
||||||
|
|
||||||
def ceph_snapshot_remove(config, pool, volume, snapshot):
|
def ceph_snapshot_remove(config, pool, volume, snapshot):
|
||||||
"""
|
"""
|
||||||
Remove Ceph snapshot
|
Remove Ceph snapshot
|
||||||
|
|
|
@ -1082,6 +1082,36 @@ def rename_snapshot(zkhandler, pool, volume, name, new_name):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def rollback_snapshot(zkhandler, pool, volume, name):
|
||||||
|
if not verifyVolume(zkhandler, pool, volume):
|
||||||
|
return False, 'ERROR: No volume with name "{}" is present in pool "{}".'.format(
|
||||||
|
volume, pool
|
||||||
|
)
|
||||||
|
if not verifySnapshot(zkhandler, pool, volume, name):
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
'ERROR: No snapshot with name "{}" is present for volume "{}" in pool "{}".'.format(
|
||||||
|
name, volume, pool
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# 1. Roll back the snapshot
|
||||||
|
retcode, stdout, stderr = common.run_os_command(
|
||||||
|
"rbd snap rollback {}/{}@{}".format(pool, volume, name)
|
||||||
|
)
|
||||||
|
if retcode:
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
'ERROR: Failed to roll back RBD volume "{}" in pool "{}" to snapshot "{}": {}'.format(
|
||||||
|
volume, pool, name, stderr
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return True, 'Rolled back RBD volume "{}" in pool "{}" to snapshot "{}".'.format(
|
||||||
|
volume, pool, name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def remove_snapshot(zkhandler, pool, volume, name):
|
def remove_snapshot(zkhandler, pool, volume, name):
|
||||||
if not verifyVolume(zkhandler, pool, volume):
|
if not verifyVolume(zkhandler, pool, volume):
|
||||||
return False, 'ERROR: No volume with name "{}" is present in pool "{}".'.format(
|
return False, 'ERROR: No volume with name "{}" is present in pool "{}".'.format(
|
||||||
|
|
Loading…
Reference in New Issue