Adds resource uuid, volume support to lifecycle scheduler hints
Heat resources are now assigned an orchestration id prior to their instantiation by nova, cinder, et. al. This id is now added to stack lifecycle scheduler hints. In addition to nova receiving such hints, cinder is also now supported. Change-Id: I5a13feb1bdedfbbe44de15e3d9eae72e56ec8a25 Closes-Bug: #1476345
This commit is contained in:
parent
d5c02aaba0
commit
b31259a9ed
@ -14,10 +14,10 @@
|
||||
====================================
|
||||
Heat Stack Lifecycle Scheduler Hints
|
||||
====================================
|
||||
This is a mechanism whereby when heat processes a stack Server resource, the
|
||||
stack id, root stack id, stack resource id, stack resource name and the path
|
||||
in the stack can be passed to nova by heat as scheduler hints, to the
|
||||
configured schedulers for nova.
|
||||
This is a mechanism whereby when heat processes a stack Server or Volume
|
||||
resource, the stack id, root stack id, stack resource uuid, stack resource
|
||||
name and the path in the stack can be passed to nova and cinder by heat as
|
||||
scheduler hints, to the configured schedulers for nova and cinder.
|
||||
|
||||
|
||||
Enabling the scheduler hints
|
||||
@ -28,14 +28,15 @@ set stack_scheduler_hints to True in heat.conf.
|
||||
The hints
|
||||
---------
|
||||
When heat processes a stack, and the feature is enabled, the stack id, root
|
||||
stack id, stack resource id, stack resource name and the path in the stack
|
||||
stack id, stack resource uuid, stack resource name, and the path in the stack
|
||||
(as a list of tuple, (stackresourcename, stackname)) will be passed to nova
|
||||
by heat as scheduler hints, to the configured schedulers for nova.
|
||||
and cinder by heat as scheduler hints, to the configured schedulers for
|
||||
nova and cinder.
|
||||
|
||||
Purpose
|
||||
-------
|
||||
A heat provider may have a need for custom code to examine stack requests
|
||||
prior to performing the operations to create or update a stack. After the
|
||||
custom code completes, the provider may want to provide hints to the nova
|
||||
scheduler with stack related identifiers, for processing by any custom
|
||||
scheduler plug-ins configured for nova.
|
||||
or cinder schedulers with stack related identifiers, for processing by
|
||||
any custom scheduler plug-ins configured for nova or cinder.
|
||||
|
@ -198,18 +198,20 @@ engine_opts = [
|
||||
default=False,
|
||||
help=_('When this feature is enabled, scheduler hints'
|
||||
' identifying the heat stack context of a server'
|
||||
' resource are passed to the configured schedulers in'
|
||||
' nova, for server creates done using heat resource'
|
||||
' types OS::Nova::Server and AWS::EC2::Instance.'
|
||||
' heat_root_stack_id will be set to the id of the root'
|
||||
' stack of the resource, heat_stack_id will be set to'
|
||||
' the id of the resource\'s parent stack,'
|
||||
' or volume resource are passed to the configured'
|
||||
' schedulers in nova and cinder, for creates done'
|
||||
' using heat resource types OS::Cinder::Volume,'
|
||||
' OS::Nova::Server, and AWS::EC2::Instance.'
|
||||
' heat_root_stack_id will be set to the id of the'
|
||||
' root stack of the resource, heat_stack_id will be'
|
||||
' set to the id of the resource\'s parent stack,'
|
||||
' heat_stack_name will be set to the name of the'
|
||||
' resource\'s parent stack, heat_path_in_stack will be'
|
||||
' set to a list of tuples,'
|
||||
' (stackresourcename, stackname) with list[0] being'
|
||||
' (None, rootstackname), and heat_resource_name will'
|
||||
' be set to the resource\'s name.')),
|
||||
' resource\'s parent stack, heat_path_in_stack will'
|
||||
' be set to a list of tuples, (stackresourcename,'
|
||||
' stackname) with list[0] being (None, rootstackname),'
|
||||
' heat_resource_name will be set to the resource\'s'
|
||||
' name, and heat_resource_uuid will be set to the'
|
||||
' resource\'s orchestration id.')),
|
||||
cfg.BoolOpt('encrypt_parameters_and_properties',
|
||||
default=False,
|
||||
help=_('Encrypt template parameters that were marked as'
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
@ -26,13 +25,12 @@ from heat.engine.clients import progress
|
||||
from heat.engine import constraints
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
|
||||
cfg.CONF.import_opt('stack_scheduler_hints', 'heat.common.config')
|
||||
from heat.engine.resources import scheduler_hints as sh
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Instance(resource.Resource):
|
||||
class Instance(resource.Resource, sh.SchedulerHintsMixin):
|
||||
|
||||
PROPERTIES = (
|
||||
IMAGE_ID, INSTANCE_TYPE, KEY_NAME, AVAILABILITY_ZONE,
|
||||
@ -529,14 +527,7 @@ class Instance(resource.Resource):
|
||||
scheduler_hints[hint] = hint_value
|
||||
else:
|
||||
scheduler_hints = None
|
||||
if cfg.CONF.stack_scheduler_hints:
|
||||
if scheduler_hints is None:
|
||||
scheduler_hints = {}
|
||||
scheduler_hints['heat_root_stack_id'] = self.stack.root_stack_id()
|
||||
scheduler_hints['heat_stack_id'] = self.stack.id
|
||||
scheduler_hints['heat_stack_name'] = self.stack.name
|
||||
scheduler_hints['heat_path_in_stack'] = self.stack.path_in_stack()
|
||||
scheduler_hints['heat_resource_name'] = self.name
|
||||
scheduler_hints = self._scheduler_hints(scheduler_hints)
|
||||
|
||||
nics = self._build_nics(self.properties[self.NETWORK_INTERFACES],
|
||||
security_groups=security_groups,
|
||||
|
@ -23,13 +23,14 @@ from heat.engine.clients import progress
|
||||
from heat.engine import constraints
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
from heat.engine.resources import scheduler_hints as sh
|
||||
from heat.engine.resources import volume_base as vb
|
||||
from heat.engine import support
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CinderVolume(vb.BaseVolume):
|
||||
class CinderVolume(vb.BaseVolume, sh.SchedulerHintsMixin):
|
||||
|
||||
PROPERTIES = (
|
||||
AVAILABILITY_ZONE, SIZE, SNAPSHOT_ID, BACKUP_ID, NAME,
|
||||
@ -233,8 +234,14 @@ class CinderVolume(vb.BaseVolume):
|
||||
def _create_arguments(self):
|
||||
arguments = {
|
||||
'size': self.properties[self.SIZE],
|
||||
'availability_zone': self.properties[self.AVAILABILITY_ZONE]
|
||||
'availability_zone': self.properties[self.AVAILABILITY_ZONE],
|
||||
}
|
||||
|
||||
scheduler_hints = self._scheduler_hints(
|
||||
self.properties[self.CINDER_SCHEDULER_HINTS])
|
||||
if scheduler_hints:
|
||||
arguments[self.CINDER_SCHEDULER_HINTS] = scheduler_hints
|
||||
|
||||
if self.properties[self.IMAGE]:
|
||||
arguments['imageRef'] = self.client_plugin('glance').get_image_id(
|
||||
self.properties[self.IMAGE])
|
||||
@ -242,7 +249,7 @@ class CinderVolume(vb.BaseVolume):
|
||||
arguments['imageRef'] = self.properties[self.IMAGE_REF]
|
||||
|
||||
optionals = (self.SNAPSHOT_ID, self.VOLUME_TYPE, self.SOURCE_VOLID,
|
||||
self.METADATA, self.CINDER_SCHEDULER_HINTS)
|
||||
self.METADATA)
|
||||
arguments.update((prop, self.properties[prop]) for prop in optionals
|
||||
if self.properties[prop])
|
||||
|
||||
|
@ -31,17 +31,17 @@ from heat.engine import function
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
from heat.engine.resources.openstack.neutron import subnet
|
||||
from heat.engine.resources import scheduler_hints as sh
|
||||
from heat.engine.resources import stack_user
|
||||
from heat.engine import support
|
||||
from heat.rpc import api as rpc_api
|
||||
|
||||
cfg.CONF.import_opt('default_software_config_transport', 'heat.common.config')
|
||||
cfg.CONF.import_opt('stack_scheduler_hints', 'heat.common.config')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Server(stack_user.StackUser):
|
||||
class Server(stack_user.StackUser, sh.SchedulerHintsMixin):
|
||||
|
||||
PROPERTIES = (
|
||||
NAME, IMAGE, BLOCK_DEVICE_MAPPING, BLOCK_DEVICE_MAPPING_V2,
|
||||
@ -706,15 +706,9 @@ class Server(stack_user.StackUser):
|
||||
instance_meta = self.client_plugin().meta_serialize(
|
||||
instance_meta)
|
||||
|
||||
scheduler_hints = self.properties[self.SCHEDULER_HINTS]
|
||||
if cfg.CONF.stack_scheduler_hints:
|
||||
if scheduler_hints is None:
|
||||
scheduler_hints = {}
|
||||
scheduler_hints['heat_root_stack_id'] = self.stack.root_stack_id()
|
||||
scheduler_hints['heat_stack_id'] = self.stack.id
|
||||
scheduler_hints['heat_stack_name'] = self.stack.name
|
||||
scheduler_hints['heat_path_in_stack'] = self.stack.path_in_stack()
|
||||
scheduler_hints['heat_resource_name'] = self.name
|
||||
scheduler_hints = self._scheduler_hints(
|
||||
self.properties[self.SCHEDULER_HINTS])
|
||||
|
||||
nics = self._build_nics(self.properties[self.NETWORKS])
|
||||
block_device_mapping = self._build_block_device_mapping(
|
||||
self.properties[self.BLOCK_DEVICE_MAPPING])
|
||||
|
45
heat/engine/resources/scheduler_hints.py
Normal file
45
heat/engine/resources/scheduler_hints.py
Normal file
@ -0,0 +1,45 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
cfg.CONF.import_opt('stack_scheduler_hints', 'heat.common.config')
|
||||
|
||||
|
||||
class SchedulerHintsMixin(object):
|
||||
'''
|
||||
Utility class to encapsulate Scheduler Hint related logic shared
|
||||
between resources.
|
||||
'''
|
||||
|
||||
HEAT_ROOT_STACK_ID = 'heat_root_stack_id'
|
||||
HEAT_STACK_ID = 'heat_stack_id'
|
||||
HEAT_STACK_NAME = 'heat_stack_name'
|
||||
HEAT_PATH_IN_STACK = 'heat_path_in_stack'
|
||||
HEAT_RESOURCE_NAME = 'heat_resource_name'
|
||||
HEAT_RESOURCE_UUID = 'heat_resource_uuid'
|
||||
|
||||
def _scheduler_hints(self, scheduler_hints):
|
||||
'''Augment scheduler hints with supplemental content.'''
|
||||
if cfg.CONF.stack_scheduler_hints:
|
||||
if scheduler_hints is None:
|
||||
scheduler_hints = {}
|
||||
scheduler_hints[self.HEAT_ROOT_STACK_ID] = \
|
||||
self.stack.root_stack_id()
|
||||
scheduler_hints[self.HEAT_STACK_ID] = self.stack.id
|
||||
scheduler_hints[self.HEAT_STACK_NAME] = self.stack.name
|
||||
scheduler_hints[self.HEAT_PATH_IN_STACK] = \
|
||||
self.stack.path_in_stack()
|
||||
scheduler_hints[self.HEAT_RESOURCE_NAME] = self.name
|
||||
scheduler_hints[self.HEAT_RESOURCE_UUID] = self.uuid
|
||||
return scheduler_hints
|
@ -30,6 +30,7 @@ from heat.engine.clients import progress
|
||||
from heat.engine import environment
|
||||
from heat.engine import resource
|
||||
from heat.engine.resources.aws.ec2 import instance as instances
|
||||
from heat.engine.resources import scheduler_hints as sh
|
||||
from heat.engine import scheduler
|
||||
from heat.engine import stack as parser
|
||||
from heat.engine import template
|
||||
@ -581,7 +582,7 @@ class InstancesTest(common.HeatTestCase):
|
||||
|
||||
def test_instance_create_with_stack_scheduler_hints(self):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
instances.cfg.CONF.set_override('stack_scheduler_hints', True)
|
||||
sh.cfg.CONF.set_override('stack_scheduler_hints', True)
|
||||
# Unroll _create_test_instance, to enable check
|
||||
# for addition of heat ids (stack id, resource name)
|
||||
stack_name = 'test_instance_create_with_stack_scheduler_hints'
|
||||
@ -592,11 +593,16 @@ class InstancesTest(common.HeatTestCase):
|
||||
bdm = {"vdb": "9ef5496e-7426-446a-bbc8-01f84d9c9972:snap::True"}
|
||||
self._mock_get_image_id_success('CentOS 5.2', 1)
|
||||
|
||||
# instance.uuid is only available once the resource has been added.
|
||||
stack.add_resource(instance)
|
||||
self.assertIsNotNone(instance.uuid)
|
||||
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().AndReturn(self.fc)
|
||||
self.stub_SnapshotConstraint_validate()
|
||||
|
||||
self.m.StubOutWithMock(self.fc.servers, 'create')
|
||||
shm = sh.SchedulerHintsMixin
|
||||
self.fc.servers.create(
|
||||
image=1, flavor=1, key_name='test',
|
||||
name=utils.PhysName(
|
||||
@ -605,11 +611,12 @@ class InstancesTest(common.HeatTestCase):
|
||||
limit=instance.physical_resource_name_limit),
|
||||
security_groups=None,
|
||||
userdata=mox.IgnoreArg(),
|
||||
scheduler_hints={'heat_root_stack_id': stack.root_stack_id(),
|
||||
'heat_stack_id': stack.id,
|
||||
'heat_stack_name': stack.name,
|
||||
'heat_path_in_stack': [(None, stack.name)],
|
||||
'heat_resource_name': instance.name,
|
||||
scheduler_hints={shm.HEAT_ROOT_STACK_ID: stack.root_stack_id(),
|
||||
shm.HEAT_STACK_ID: stack.id,
|
||||
shm.HEAT_STACK_NAME: stack.name,
|
||||
shm.HEAT_PATH_IN_STACK: [(None, stack.name)],
|
||||
shm.HEAT_RESOURCE_NAME: instance.name,
|
||||
shm.HEAT_RESOURCE_UUID: instance.uuid,
|
||||
'foo': ['spam', 'ham', 'baz'], 'bar': 'eggs'},
|
||||
meta=None, nics=None, availability_zone=None,
|
||||
block_device_mapping=bdm).AndReturn(
|
||||
|
@ -23,6 +23,7 @@ from heat.common import template_format
|
||||
from heat.engine.clients.os import cinder
|
||||
from heat.engine.clients.os import glance
|
||||
from heat.engine.resources.openstack.cinder import volume as c_vol
|
||||
from heat.engine.resources import scheduler_hints as sh
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import scheduler
|
||||
from heat.objects import resource_data as resource_data_object
|
||||
@ -887,6 +888,42 @@ class CinderVolumeTest(vt_base.BaseVolumeTest):
|
||||
'volume API.', six.text_type(ex))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_cinder_create_with_stack_scheduler_hints(self):
|
||||
fv = vt_base.FakeVolume('creating')
|
||||
sh.cfg.CONF.set_override('stack_scheduler_hints', True)
|
||||
|
||||
stack_name = 'test_cvolume_stack_scheduler_hints_stack'
|
||||
t = template_format.parse(single_cinder_volume_template)
|
||||
stack = utils.parse_stack(t, stack_name=stack_name)
|
||||
|
||||
rsrc = stack['volume']
|
||||
|
||||
# rsrc.uuid is only available once the resource has been added.
|
||||
stack.add_resource(rsrc)
|
||||
self.assertIsNotNone(rsrc.uuid)
|
||||
|
||||
cinder.CinderClientPlugin._create().AndReturn(self.cinder_fc)
|
||||
shm = sh.SchedulerHintsMixin
|
||||
self.cinder_fc.volumes.create(
|
||||
size=1, name='test_name', description='test_description',
|
||||
availability_zone=None,
|
||||
scheduler_hints={shm.HEAT_ROOT_STACK_ID: stack.root_stack_id(),
|
||||
shm.HEAT_STACK_ID: stack.id,
|
||||
shm.HEAT_STACK_NAME: stack.name,
|
||||
shm.HEAT_PATH_IN_STACK: [(None, stack.name)],
|
||||
shm.HEAT_RESOURCE_NAME: rsrc.name,
|
||||
shm.HEAT_RESOURCE_UUID: rsrc.uuid}).AndReturn(fv)
|
||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
||||
fv_ready = vt_base.FakeVolume('available', id=fv.id)
|
||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv_ready)
|
||||
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
# this makes sure the auto increment worked on volume creation
|
||||
self.assertTrue(rsrc.id > 0)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def _test_cinder_create_invalid_property_combinations(
|
||||
self, stack_name, combinations, err_msg, exc):
|
||||
stack = utils.parse_stack(self.t, stack_name=stack_name)
|
||||
|
@ -32,6 +32,7 @@ from heat.engine.clients.os import zaqar
|
||||
from heat.engine import environment
|
||||
from heat.engine import resource
|
||||
from heat.engine.resources.openstack.nova import server as servers
|
||||
from heat.engine.resources import scheduler_hints as sh
|
||||
from heat.engine import scheduler
|
||||
from heat.engine import stack as parser
|
||||
from heat.engine import template
|
||||
@ -979,7 +980,7 @@ class ServersTest(common.HeatTestCase):
|
||||
def test_server_create_with_stack_scheduler_hints(self):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.id = '5678'
|
||||
servers.cfg.CONF.set_override('stack_scheduler_hints', True)
|
||||
sh.cfg.CONF.set_override('stack_scheduler_hints', True)
|
||||
# Unroll _create_test_server, to enable check
|
||||
# for addition of heat ids (stack id, resource name)
|
||||
stack_name = 'test_server_w_stack_sched_hints_s'
|
||||
@ -990,22 +991,28 @@ class ServersTest(common.HeatTestCase):
|
||||
server = servers.Server(server_name,
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
# server.uuid is only available once the resource has been added.
|
||||
stack.add_resource(server)
|
||||
self.assertIsNotNone(server.uuid)
|
||||
|
||||
self._mock_get_image_id_success('CentOS 5.2', 1)
|
||||
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
self.m.StubOutWithMock(self.fc.servers, 'create')
|
||||
shm = sh.SchedulerHintsMixin
|
||||
self.fc.servers.create(
|
||||
image=1, flavor=1, key_name='test',
|
||||
name=server_name,
|
||||
security_groups=[],
|
||||
userdata=mox.IgnoreArg(),
|
||||
scheduler_hints={'heat_root_stack_id': stack.root_stack_id(),
|
||||
'heat_stack_id': stack.id,
|
||||
'heat_stack_name': stack.name,
|
||||
'heat_path_in_stack': [(None, stack.name)],
|
||||
'heat_resource_name': server.name},
|
||||
scheduler_hints={shm.HEAT_ROOT_STACK_ID: stack.root_stack_id(),
|
||||
shm.HEAT_STACK_ID: stack.id,
|
||||
shm.HEAT_STACK_NAME: stack.name,
|
||||
shm.HEAT_PATH_IN_STACK: [(None, stack.name)],
|
||||
shm.HEAT_RESOURCE_NAME: server.name,
|
||||
shm.HEAT_RESOURCE_UUID: server.uuid},
|
||||
meta=None, nics=None, availability_zone=None,
|
||||
block_device_mapping=None, block_device_mapping_v2=None,
|
||||
config_drive=None, disk_config=None, reservation_id=None,
|
||||
|
Loading…
Reference in New Issue
Block a user