if key_name:
nova_utils.get_keypair(self.nova(), key_name)
+ # either volume_id or snapshot_id needs to be specified, but not both
+ # for block device mapping.
+ bdm = self.properties.get('block_device_mapping') or []
+ bootable_vol = False
+ for mapping in bdm:
+ if mapping['device_name'] is 'vda':
+ bootable_vol = True
+
+ if mapping.get('volume_id') and mapping.get('snapshot_id'):
+ raise exception.ResourcePropertyConflict('volume_id',
+ 'snapshot_id')
+ if not mapping.get('volume_id') and not mapping.get('snapshot_id'):
+ msg = _('Either volume_id or snapshot_id must be specified for'
+ ' device mapping %s') % mapping['device_name']
+ raise exception.StackValidationFailed(message=msg)
+
# make sure the image exists if specified.
image = self.properties.get('image', None)
if image:
nova_utils.get_image_id(self.nova(), image)
- else:
- # TODO(sbaker) confirm block_device_mapping is populated
- # for boot-by-volume (see LP bug #1215267)
- pass
+ elif not image and not bootable_vol:
+ msg = _('Neither image nor bootable volume is specified for'
+ ' instance %s') % self.name
+ raise exception.StackValidationFailed(message=msg)
def handle_delete(self):
'''
'delete_on_termination': True
}
]))
+
+ def test_validate_conflict_block_device_mapping_props(self):
+ stack_name = 'test_validate_conflict_block_device_mapping_props'
+ (t, stack) = self._setup_test_stack(stack_name)
+
+ bdm = [{'device_name': 'vdb', 'snapshot_id': '1234',
+ 'volume_id': '1234'}]
+ t['Resources']['WebServer']['Properties']['block_device_mapping'] = bdm
+ server = servers.Server('server_create_image_err',
+ t['Resources']['WebServer'], stack)
+ self.m.StubOutWithMock(server, 'nova')
+ server.nova().MultipleTimes().AndReturn(self.fc)
+ self.m.ReplayAll()
+
+ self.assertRaises(exception.ResourcePropertyConflict, server.validate)
+ self.m.VerifyAll()
+
+ def test_validate_insufficient_block_device_mapping_props(self):
+ stack_name = 'test_validate_insufficient_block_device_mapping_props'
+ (t, stack) = self._setup_test_stack(stack_name)
+
+ bdm = [{'device_name': 'vdb', 'volume_size': '1',
+ 'delete_on_termination': True}]
+ t['Resources']['WebServer']['Properties']['block_device_mapping'] = bdm
+ server = servers.Server('server_create_image_err',
+ t['Resources']['WebServer'], stack)
+ self.m.StubOutWithMock(server, 'nova')
+ server.nova().MultipleTimes().AndReturn(self.fc)
+ self.m.ReplayAll()
+
+ ex = self.assertRaises(exception.StackValidationFailed,
+ server.validate)
+ msg = 'Either volume_id or snapshot_id must be specified for device' +\
+ ' mapping vdb'
+ self.assertEqual(msg, str(ex))
+
+ self.m.VerifyAll()
+
+ def test_validate_without_image_or_bootable_volume(self):
+ stack_name = 'test_validate_without_image_or_bootable_volume'
+ (t, stack) = self._setup_test_stack(stack_name)
+
+ del t['Resources']['WebServer']['Properties']['image']
+ bdm = [{'device_name': 'vdb', 'volume_id': '1234'}]
+ t['Resources']['WebServer']['Properties']['block_device_mapping'] = bdm
+ server = servers.Server('server_create_image_err',
+ t['Resources']['WebServer'], stack)
+ self.m.StubOutWithMock(server, 'nova')
+ server.nova().MultipleTimes().AndReturn(self.fc)
+ self.m.ReplayAll()
+
+ ex = self.assertRaises(exception.StackValidationFailed,
+ server.validate)
+ msg = 'Neither image nor bootable volume is specified for instance %s'\
+ % server.name
+ self.assertEqual(msg, str(ex))
+
+ self.m.VerifyAll()