I have wrote a script that deletes the snapshots that are created for the backups. Except the last backup snapshot.
#!/usr/bin/python3
import rados
import sys
import rbd
import re
import time
"""
Volumes can have two types of names:
volume-5b2851f8-4722-42c2-a6dd-4dc359afbf6c
volume-81895053-a4d4-4cdb-9e1f-4e8340d56949.deleted
Volumes with the suffix deleted, are volumes that are deleted but have related glance images to it or have childeren to a new cinder volumes.
A Cinder volume can have snapshots, Glance images and cinder backup.
In Ceph a cinder snapshot, this is a Ceph snapshot with the prefix of the name "snapshot-", with the protection set and the ID of the snapshot:
snapshot-722ad9a3-e1bf-416f-81d4-01a4f32c02ee
When a Glance image gets created based from a Cinder Volume, a new cinder volume gets created and in Ceph a new snapshot gets
created with the name volume-<UUID of of the new cinder_volume>.clone_snap
When a cinder-backup full backup gets created.
On the source ceph cluster the image gets a snapshot with syntax "backup.<UUID of the backup>.snap.<timestamp>":
backup.03fc2dfb-9654-4f00-ac11-a8f471c599d0.snap.1625049437.645906
On the destination ceph cluster the naming is volume-<UUID of the volume>.backup.<UUID of the backup> and the name of
the snapshot backup.<UUID of the backup>.snap.<timestamp>
When a incremental cinder backup is created
on the source the naming of the snapshot is the same as with a full backup: backup.<UUID of the backup>.snap.<timestamp>
on the destination the volume of the full backup is used and a snapshot is created with syntax "backup.<UUID of the backup>.snap.<timestamp>"
When a backup also has incremental backups you are unable to delete in Cinder the full backups.
So on the source Ceph cluster, a volume can have three types of snapshots:
snapshot-722ad9a3-e1bf-416f-81d4-01a4f32c02ee
volume-7048a7bf-530b-42ee-bfe9-6221c3b9f384.clone_snap
backup.e224e8d2-5bda-4a8a-8841-0c578737f865.snap.1625049686.5710375
"""
class Cleanup:
dryRun = True
def __init__(self): self.__cluster__ = rados.Rados(conffile='/etc/ceph/ceph.conf') print("\nlibrados version: {}".format(str(self.__cluster__.version())))
print("Will attempt to connect to: {}".format(str(self.__cluster__.conf_get('mon host'))))
## open pool self.__ioctx__ = self.__cluster__.open_ioctx('volumes') self.__rbd_inst__ = rbd.RBD() self.__allImages__ = self.__rbd_inst__.list(self.__ioctx__)
def getAllCinderVolumes(self): allFoundImages = []
for imageName in self.__allImages__:
if not re.search('^volume-[a-z0-9-]{36}$', imageName) == None: allFoundImages.append(imageName)
return allFoundImages
for snapshot in image.list_snaps():
if not re.search('^backup.[a-z0-9-]{36}\.snap\.[0-9]+\.[0-9]+$', snapshot['name']) == None: snapshots.append(snapshot['name'])
return snapshots
def deleteSnapshots(self, imageName, snapshots):
print('the following snapshots will be deleted from %s: %s' % (imageName, snapshots))
image = rbd.Image(self.__ioctx__, name=imageName)
for snapshot in snapshots: print('deleting %s of %s' % (snapshot, imageName))
if not self.dryRun: image.remove_snap(snapshot)
# sleep for X seconds to reduce the load self.sleepTime(60)
def sleepTime(self, sleepTime):
interval = 10 print('sleep for %s seconds' % (sleepTime))
while sleepTime > interval: print('waiting for %s sec' % (sleepTime)) time.sleep(interval) sleepTime = sleepTime - interval
if sleepTime > 0: print('waiting for %s sec' % (sleepTime)) time.sleep(sleepTime)
cleanup = Cleanup()
# imageName = 'volume-03ffce25-2137-40f8-be7f-d31c19c5ed55'
# snapshots = cleanup.getSnapshotsOfImageThatCanBeDeleted(imageName)
#
# if len(snapshots) > 0:
# result = cleanup.deleteSnapshots(imageName, snapshots)
for imageName in cleanup.getAllCinderVolumes():
snapshots = cleanup.getSnapshotsOfImageThatCanBeDeleted(imageName)
if len(snapshots) > 0:
result = cleanup.deleteSnapshots(imageName, snapshots)
This bug started in Stein.
I have wrote a script that deletes the snapshots that are created for the backups. Except the last backup snapshot.
#!/usr/bin/python3
import rados
import sys
import rbd
import re
import time
""" 5b2851f8- 4722-42c2- a6dd-4dc359afbf 6c 81895053- a4d4-4cdb- 9e1f-4e8340d569 49.deleted
Volumes can have two types of names:
volume-
volume-
Volumes with the suffix deleted, are volumes that are deleted but have related glance images to it or have childeren to a new cinder volumes.
A Cinder volume can have snapshots, Glance images and cinder backup. 722ad9a3- e1bf-416f- 81d4-01a4f32c02 ee
In Ceph a cinder snapshot, this is a Ceph snapshot with the prefix of the name "snapshot-", with the protection set and the ID of the snapshot:
snapshot-
When a Glance image gets created based from a Cinder Volume, a new cinder volume gets created and in Ceph a new snapshot gets volume> .clone_ snap
created with the name volume-<UUID of of the new cinder_
When a cinder-backup full backup gets created. .snap.< timestamp> ": 03fc2dfb- 9654-4f00- ac11-a8f471c599 d0.snap. 1625049437. 645906
On the source ceph cluster the image gets a snapshot with syntax "backup.<UUID of the backup>
backup.
On the destination ceph cluster the naming is volume-<UUID of the volume> .backup. <UUID of the backup> and the name of .snap.< timestamp>
the snapshot backup.<UUID of the backup>
When a incremental cinder backup is created .snap.< timestamp> .snap.< timestamp> "
on the source the naming of the snapshot is the same as with a full backup: backup.<UUID of the backup>
on the destination the volume of the full backup is used and a snapshot is created with syntax "backup.<UUID of the backup>
When a backup also has incremental backups you are unable to delete in Cinder the full backups.
So on the source Ceph cluster, a volume can have three types of snapshots: 722ad9a3- e1bf-416f- 81d4-01a4f32c02 ee 7048a7bf- 530b-42ee- bfe9-6221c3b9f3 84.clone_ snap e224e8d2- 5bda-4a8a- 8841-0c578737f8 65.snap. 1625049686. 5710375
snapshot-
volume-
backup.
"""
class Cleanup:
dryRun = True
def __init__(self):
self._ _cluster_ _ = rados.Rados( conffile= '/etc/ceph/ ceph.conf' )
print( "\nlibrados version: {}".format( str(self. __cluster_ _.version( )))) str(self. __cluster_ _.conf_ get('mon host'))))
print("Will attempt to connect to: {}".format(
## open pool
self._ _ioctx_ _ = self.__ cluster_ _.open_ ioctx(' volumes' )
self._ _rbd_inst_ _ = rbd.RBD()
self._ _allImages_ _ = self.__ rbd_inst_ _.list( self.__ ioctx__ )
def getAllCinderVol umes(self) :
allFoundImages = [] '^volume- [a-z0-9- ]{36}$' , imageName) == None:
allFoundImage s.append( imageName)
for imageName in self.__allImages__:
if not re.search(
return allFoundImages
def getSnapshotsOfI mage(self, imageName): self.__ ioctx__ , name=imageName)
snapshots = []
image = rbd.Image(
for snapshot in image.list_snaps(): '^backup. [a-z0-9- ]{36}\. snap\.[ 0-9]+\. [0-9]+$ ', snapshot['name']) == None:
snapshots. append( snapshot[ 'name'] )
if not re.search(
return snapshots
def __findLatestSna pshot__ (self, snapshots):
latest[ 'timestamp' ] = 0
latest[ 'name'] = ''
snapshotTi mestamp = float(snapshot. split(' .',3)[3] )
latest[ 'timestamp' ] = snapshotTimestamp
latest[ 'name'] = snapshot
latest = {}
for snapshot in snapshots:
if latest['timestamp'] <= snapshotTimestamp:
return latest['name']
def getSnapshotsOfI mageThatCanBeDe leted(self, imageName): tsOfImage( imageName) findLatestSnaps hot__(snapshots )
snapshots. remove( latest)
snapshots = self.getSnapsho
# not snapshots == None
if len(snapshots) > 0:
latest = self.__
return snapshots
else:
return []
def deleteSnapshots (self, imageName, snapshots):
print('the following snapshots will be deleted from %s: %s' % (imageName, snapshots))
image = rbd.Image( self.__ ioctx__ , name=imageName)
print( 'deleting %s of %s' % (snapshot, imageName))
image. remove_ snap(snapshot)
for snapshot in snapshots:
if not self.dryRun:
# sleep for X seconds to reduce the load
self. sleepTime( 60)
def sleepTime(self, sleepTime):
print( 'sleep for %s seconds' % (sleepTime))
print( 'waiting for %s sec' % (sleepTime))
time. sleep(interval)
sleepTime = sleepTime - interval
print( 'waiting for %s sec' % (sleepTime))
time. sleep(sleepTime )
interval = 10
while sleepTime > interval:
if sleepTime > 0:
cleanup = Cleanup()
# imageName = 'volume- 03ffce25- 2137-40f8- be7f-d31c19c5ed 55' getSnapshotsOfI mageThatCanBeDe leted(imageName ) deleteSnapshots (imageName, snapshots)
# snapshots = cleanup.
#
# if len(snapshots) > 0:
# result = cleanup.
for imageName in cleanup. getAllCinderVol umes(): getSnapshotsOfI mageThatCanBeDe leted(imageName )
snapshots = cleanup.
if len(snapshots) > 0: deleteSnapshots (imageName, snapshots)
result = cleanup.