Merge "Handle missing volume in handle_snapshot_delete"

This commit is contained in:
Zuul
2025-08-22 17:03:49 +00:00
committed by Gerrit Code Review
2 changed files with 99 additions and 2 deletions

View File

@@ -12,6 +12,7 @@
# under the License. # under the License.
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging
from heat.common import exception from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
@@ -19,6 +20,8 @@ from heat.engine.clients import progress
from heat.engine import resource from heat.engine import resource
from heat.engine import rsrc_defn from heat.engine import rsrc_defn
LOG = logging.getLogger(__name__)
class BaseVolume(resource.Resource): class BaseVolume(resource.Resource):
"""Base Volume Manager.""" """Base Volume Manager."""
@@ -95,8 +98,13 @@ class BaseVolume(resource.Resource):
return prg return prg
def _create_backup(self): def _create_backup(self):
backup = self.client().backups.create(self.resource_id) with self.client_plugin().ignore_not_found:
return backup.id backup = self.client().backups.create(self.resource_id)
return backup.id
LOG.debug(
"Skip creating a backup for volume %s - NotFound",
self.resource_id)
return None
def _check_create_backup_complete(self, prg): def _check_create_backup_complete(self, prg):
backup = self.client().backups.get(prg.backup_id) backup = self.client().backups.get(prg.backup_id)
@@ -133,6 +141,8 @@ class BaseVolume(resource.Resource):
if not prg.backup['called']: if not prg.backup['called']:
prg.backup_id = self._create_backup() prg.backup_id = self._create_backup()
prg.backup['called'] = True prg.backup['called'] = True
if prg.backup_id is None:
prg.backup['complete'] = True
return False return False
if not prg.backup['complete']: if not prg.backup['complete']:

View File

@@ -25,6 +25,7 @@ from heat.common import template_format
from heat.engine.clients.os import cinder from heat.engine.clients.os import cinder
from heat.engine.clients.os import glance from heat.engine.clients.os import glance
from heat.engine.clients.os import nova from heat.engine.clients.os import nova
from heat.engine.clients import progress
from heat.engine.resources.openstack.cinder import volume as c_vol from heat.engine.resources.openstack.cinder import volume as c_vol
from heat.engine.resources import scheduler_hints as sh from heat.engine.resources import scheduler_hints as sh
from heat.engine import rsrc_defn from heat.engine import rsrc_defn
@@ -1236,6 +1237,92 @@ class CinderVolumeTest(vt_base.VolumeTestCase):
stack) stack)
self.assertIsNone(rsrc.handle_delete_snapshot(mock_vs)) self.assertIsNone(rsrc.handle_delete_snapshot(mock_vs))
def test_check_delete_complete(self):
fb_creating = vt_base.FakeBackup('creating')
fb_available = vt_base.FakeBackup("available")
self.cinder_fc.backups.create.return_value = fb_creating
self.cinder_fc.backups.get.side_effect = [fb_creating, fb_available]
self.cinder_fc.volumes.get.side_effect = [
vt_base.FakeVolume('available'),
vt_base.FakeVolume('deleting'),
cinder_exp.NotFound("Nope")
]
# will need backup first
fake_prg = progress.VolumeDeleteProgress()
self.stack_name = 'test_volume_check_delete'
t = template_format.parse(single_cinder_volume_template)
stack = utils.parse_stack(t, stack_name=self.stack_name)
rsrc = c_vol.CinderVolume(
'volume',
stack.t.resource_definitions(stack)['volume'],
stack)
# first backup was called
self.assertFalse(rsrc.check_delete_complete(fake_prg))
self.assertTrue(fake_prg.backup["called"])
self.assertFalse(fake_prg.backup["complete"])
self.assertFalse(fake_prg.delete["called"])
self.assertFalse(fake_prg.delete["complete"])
self.assertEqual(fake_prg.backup_id, fb_creating.id)
self.cinder_fc.backups.create.assert_called_once_with(rsrc.resource_id)
# polling for backup status
self.assertFalse(rsrc.check_delete_complete(fake_prg))
self.assertTrue(fake_prg.backup["called"])
self.assertFalse(fake_prg.backup["complete"])
self.assertFalse(fake_prg.delete["called"])
self.assertFalse(fake_prg.delete["complete"])
self.cinder_fc.backups.get.assert_called_once_with(fb_creating.id)
self.assertFalse(rsrc.check_delete_complete(fake_prg))
self.assertTrue(fake_prg.backup["called"])
self.assertTrue(fake_prg.backup["complete"])
self.assertFalse(fake_prg.delete["called"])
self.assertFalse(fake_prg.delete["complete"])
self.cinder_fc.backups.get.assert_called_with(fb_creating.id)
self.assertEqual(2, self.cinder_fc.backups.get.call_count)
# volume delete called, includes one volume get too
self.assertFalse(rsrc.check_delete_complete(fake_prg))
self.assertTrue(fake_prg.backup["called"])
self.assertTrue(fake_prg.backup["complete"])
self.assertTrue(fake_prg.delete["called"])
self.assertFalse(fake_prg.delete["complete"])
self.cinder_fc.volumes.delete.assert_called_once_with(rsrc.resource_id)
self.cinder_fc.volumes.get.assert_called_with(rsrc.resource_id)
self.assertEqual(1, self.cinder_fc.volumes.get.call_count)
# polling for volume deleted
self.assertFalse(rsrc.check_delete_complete(fake_prg))
self.assertTrue(fake_prg.backup["called"])
self.assertTrue(fake_prg.backup["complete"])
self.assertTrue(fake_prg.delete["called"])
self.assertFalse(fake_prg.delete["complete"])
self.cinder_fc.volumes.get.assert_called_with(rsrc.resource_id)
self.assertEqual(2, self.cinder_fc.volumes.get.call_count)
self.assertTrue(rsrc.check_delete_complete(fake_prg))
self.assertTrue(fake_prg.backup["called"])
self.assertTrue(fake_prg.backup["complete"])
self.assertTrue(fake_prg.delete["called"])
self.assertTrue(fake_prg.delete["complete"])
self.cinder_fc.volumes.get.assert_called_with(rsrc.resource_id)
self.assertEqual(3, self.cinder_fc.volumes.get.call_count)
def test_check_delete_complete_snapshot_missing_volume(self):
self.cinder_fc.backups.create.side_effect = cinder_exp.NotFound("Nope")
fake_prg = progress.VolumeDeleteProgress()
self.stack_name = 'test_check_delete_complete_snapshot_missing_volume'
t = template_format.parse(single_cinder_volume_template)
stack = utils.parse_stack(t, stack_name=self.stack_name)
rsrc = c_vol.CinderVolume(
'volume',
stack.t.resource_definitions(stack)['volume'],
stack)
self.assertFalse(rsrc.check_delete_complete(fake_prg))
self.assertTrue(fake_prg.backup["called"])
self.assertTrue(fake_prg.backup["complete"])
self.assertIsNone(fake_prg.backup_id)
self.cinder_fc.backups.create.assert_called_once_with(rsrc.resource_id)
def test_vaildate_deletion_policy(self): def test_vaildate_deletion_policy(self):
cfg.CONF.set_override('backups_enabled', False, group='volumes') cfg.CONF.set_override('backups_enabled', False, group='volumes')
self.stack_name = 'test_volume_validate_deletion_policy' self.stack_name = 'test_volume_validate_deletion_policy'