diff --git a/devstack/lib/heat b/devstack/lib/heat index 59b7cf2b59..47f6048c95 100644 --- a/devstack/lib/heat +++ b/devstack/lib/heat @@ -195,6 +195,10 @@ function configure_heat { iniset $HEAT_CONF cache enabled "True" iniset $HEAT_CONF cache backend "dogpile.cache.memory" + if ! is_service_enabled c-bak; then + iniset $HEAT_CONF volumes backups_enabled false + fi + sudo install -d -o $STACK_USER $HEAT_ENV_DIR $HEAT_TEMPLATES_DIR # copy the default environment diff --git a/heat/common/config.py b/heat/common/config.py index d4538fb570..efb1cffe33 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -344,6 +344,14 @@ revision_opts = [ 'separately, you can move this section to a different ' 'file and add it as another config option.'))] +volumes_group = cfg.OptGroup('volumes') +volumes_opts = [ + cfg.BoolOpt('backups_enabled', + default=True, + help=_("Indicate if cinder-backup service is enabled. " + "This is a temporary workaround until cinder-backup " + "service becomes discoverable, see LP#1334856."))] + def startup_sanity_check(): if (not cfg.CONF.stack_user_domain_id and @@ -377,6 +385,7 @@ def list_opts(): yield paste_deploy_group.name, paste_deploy_opts yield auth_password_group.name, auth_password_opts yield revision_group.name, revision_opts + yield volumes_group.name, volumes_opts yield profiler.list_opts()[0] yield 'clients', default_clients_opts diff --git a/heat/engine/resources/volume_base.py b/heat/engine/resources/volume_base.py index 5334a0a61d..b3ad01882b 100644 --- a/heat/engine/resources/volume_base.py +++ b/heat/engine/resources/volume_base.py @@ -11,10 +11,13 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg + from heat.common import exception from heat.common.i18n import _ from heat.engine.clients import progress from heat.engine import resource +from heat.engine import rsrc_defn class BaseVolume(resource.Resource): @@ -159,6 +162,17 @@ class BaseVolume(resource.Resource): return False return True + @classmethod + def validate_deletion_policy(cls, policy): + res = super(BaseVolume, cls).validate_deletion_policy(policy) + if res: + return res + if (policy == rsrc_defn.ResourceDefinition.SNAPSHOT and + not cfg.CONF.volumes.backups_enabled): + msg = _('"%s" deletion policy not supported - ' + 'volume backup service is not enabled.') % policy + raise exception.StackValidationFailed(message=msg) + class BaseVolumeAttachment(resource.Resource): """Base Volume Attachment Manager.""" diff --git a/heat/tests/aws/test_volume.py b/heat/tests/aws/test_volume.py index db7fb92073..4193f6806b 100644 --- a/heat/tests/aws/test_volume.py +++ b/heat/tests/aws/test_volume.py @@ -736,3 +736,14 @@ class VolumeTest(vt_base.BaseVolumeTest): six.text_type(ex)) self.assertEqual((rsrc.UPDATE, rsrc.FAILED), rsrc.state) self.m.VerifyAll() + + def test_vaildate_deletion_policy(self): + cfg.CONF.set_override('backups_enabled', False, group='volumes') + stack_name = 'test_volume_validate_deletion_policy' + self.t['Resources']['DataVolume']['DeletionPolicy'] = 'Snapshot' + stack = utils.parse_stack(self.t, stack_name=stack_name) + rsrc = self.get_volume(self.t, stack, 'DataVolume') + self.assertRaisesRegex( + exception.StackValidationFailed, + 'volume backup service is not enabled', + rsrc.validate) diff --git a/heat/tests/openstack/cinder/test_volume.py b/heat/tests/openstack/cinder/test_volume.py index 9edb650226..61032fe1b4 100644 --- a/heat/tests/openstack/cinder/test_volume.py +++ b/heat/tests/openstack/cinder/test_volume.py @@ -16,6 +16,7 @@ import copy import json from cinderclient import exceptions as cinder_exp +from oslo_config import cfg import six from heat.common import exception @@ -1247,3 +1248,14 @@ class CinderVolumeTest(vt_base.BaseVolumeTest): stack.t.resource_definitions(stack)['volume'], stack) self.assertIsNone(rsrc.handle_delete_snapshot(mock_vs)) + + def test_vaildate_deletion_policy(self): + cfg.CONF.set_override('backups_enabled', False, group='volumes') + stack_name = 'test_volume_validate_deletion_policy' + self.t['resources']['volume']['deletion_policy'] = 'Snapshot' + stack = utils.parse_stack(self.t, stack_name=stack_name) + rsrc = self.get_volume(self.t, stack, 'volume') + self.assertRaisesRegex( + exception.StackValidationFailed, + 'volume backup service is not enabled', + rsrc.validate) diff --git a/heat/tests/openstack/cinder/test_volume_utils.py b/heat/tests/openstack/cinder/test_volume_utils.py index daa639b7af..05b1f02d4c 100644 --- a/heat/tests/openstack/cinder/test_volume_utils.py +++ b/heat/tests/openstack/cinder/test_volume_utils.py @@ -64,16 +64,20 @@ class BaseVolumeTest(common.HeatTestCase): self.cinder_fc.volumes.get(fva.id).AndReturn(fv_ready) return fv_ready - def create_volume(self, t, stack, resource_name): + def get_volume(self, t, stack, resource_name): if self.use_cinder: Volume = os_vol.CinderVolume else: data = t['Resources'][resource_name] data['Properties']['AvailabilityZone'] = 'nova' Volume = aws_vol.Volume - rsrc = Volume(resource_name, - stack.t.resource_definitions(stack)[resource_name], - stack) + vol = Volume(resource_name, + stack.t.resource_definitions(stack)[resource_name], + stack) + return vol + + def create_volume(self, t, stack, resource_name): + rsrc = self.get_volume(t, stack, resource_name) self.assertIsNone(rsrc.validate()) scheduler.TaskRunner(rsrc.create)() self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)