Implements stack lifecycle scheduler hints
This is a mechanism whereby when heat processes a stack VM resource, the stack id, root stack id, stack resource id, stack resource name and the path in the stack (as a list of tuples, (stackresourcename, stackname)) can be passed to nova by heat as scheduler hints, to the configured schedulers for nova. Implements: blueprint stack-lifecycle-scheduler-hint Co-Authored-By: Karolyn Chambers <chamberk@us.ibm.com> Change-Id: I3e006339a41c469451bc3ee740018b285d3e0a65
This commit is contained in:
parent
9a58a472ea
commit
2c1dc724ca
@ -53,6 +53,7 @@ Developers Documentation
|
||||
|
||||
architecture
|
||||
pluginguide
|
||||
schedulerhints
|
||||
|
||||
API Documentation
|
||||
========================
|
||||
|
41
doc/source/schedulerhints.rst
Normal file
41
doc/source/schedulerhints.rst
Normal file
@ -0,0 +1,41 @@
|
||||
..
|
||||
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.
|
||||
|
||||
====================================
|
||||
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.
|
||||
|
||||
|
||||
Enabling the scheduler hints
|
||||
----------------------------
|
||||
By default, passing the lifecycle scheduler hints is disabled. To enable it,
|
||||
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
|
||||
(as a list of tuple, (stackresourcename, stackname)) will be passed to nova
|
||||
by heat as scheduler hints, to the configured schedulers for nova.
|
||||
|
||||
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.
|
@ -189,7 +189,24 @@ engine_opts = [
|
||||
'resource-signal using the provided keystone '
|
||||
'credentials')),
|
||||
cfg.StrOpt('onready',
|
||||
help=_('Deprecated.'))]
|
||||
help=_('Deprecated.')),
|
||||
cfg.BoolOpt('stack_scheduler_hints',
|
||||
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,'
|
||||
' 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.'))]
|
||||
|
||||
|
||||
rpc_opts = [
|
||||
cfg.StrOpt('host',
|
||||
|
@ -28,6 +28,7 @@ from heat.engine import scheduler
|
||||
from heat.engine import volume_tasks as vol_task
|
||||
|
||||
cfg.CONF.import_opt('instance_user', 'heat.common.config')
|
||||
cfg.CONF.import_opt('stack_scheduler_hints', 'heat.common.config')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -524,6 +525,14 @@ 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
|
||||
|
||||
nics = self._build_nics(self.properties[self.NETWORK_INTERFACES],
|
||||
security_groups=security_groups,
|
||||
|
@ -35,6 +35,7 @@ from heat.rpc import api as rpc_api
|
||||
|
||||
cfg.CONF.import_opt('instance_user', 'heat.common.config')
|
||||
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__)
|
||||
|
||||
@ -651,6 +652,14 @@ class Server(stack_user.StackUser):
|
||||
instance_meta)
|
||||
|
||||
scheduler_hints = self.properties.get(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
|
||||
nics = self._build_nics(self.properties.get(self.NETWORKS))
|
||||
block_device_mapping = self._build_block_device_mapping(
|
||||
self.properties.get(self.BLOCK_DEVICE_MAPPING))
|
||||
|
@ -251,6 +251,32 @@ class Stack(collections.Mapping):
|
||||
return self.parent_resource.stack.root_stack
|
||||
return self
|
||||
|
||||
def object_path_in_stack(self):
|
||||
'''
|
||||
If this is not nested return (None, self), else return stack resources
|
||||
and stacks in path from the root stack and including this stack
|
||||
|
||||
:returns: a list of (stack_resource, stack) tuples
|
||||
'''
|
||||
if self.parent_resource and self.parent_resource.stack:
|
||||
path = self.parent_resource.stack.object_path_in_stack()
|
||||
path.extend([(self.parent_resource, self)])
|
||||
return path
|
||||
return [(None, self)]
|
||||
|
||||
def path_in_stack(self):
|
||||
'''
|
||||
If this is not nested return (None, self.name), else return tuples of
|
||||
names (stack_resource.name, stack.name) in path from the root stack and
|
||||
including this stack.
|
||||
|
||||
:returns: a list of (string, string) tuples.
|
||||
|
||||
'''
|
||||
opis = self.object_path_in_stack()
|
||||
return [(stckres.name if stckres else None,
|
||||
stck.name if stck else None) for stckres, stck in opis]
|
||||
|
||||
def total_resources(self):
|
||||
'''
|
||||
Return the total number of resources in a stack, including nested
|
||||
|
@ -520,6 +520,46 @@ class InstancesTest(common.HeatTestCase):
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
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)
|
||||
# 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'
|
||||
(t, stack) = self._get_test_template(stack_name)
|
||||
resource_defns = t.resource_definitions(stack)
|
||||
instance = instances.Instance('in_create_with_sched_hints',
|
||||
resource_defns['WebServer'], stack)
|
||||
bdm = {"vdb": "9ef5496e-7426-446a-bbc8-01f84d9c9972:snap::True"}
|
||||
self._mock_get_image_id_success('CentOS 5.2', 1)
|
||||
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().AndReturn(self.fc)
|
||||
self.stub_SnapshotConstraint_validate()
|
||||
|
||||
self.m.StubOutWithMock(self.fc.servers, 'create')
|
||||
self.fc.servers.create(
|
||||
image=1, flavor=1, key_name='test',
|
||||
name=utils.PhysName(
|
||||
stack_name,
|
||||
instance.name,
|
||||
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,
|
||||
'foo': ['spam', 'ham', 'baz'], 'bar': 'eggs'},
|
||||
meta=None, nics=None, availability_zone=None,
|
||||
block_device_mapping=bdm).AndReturn(
|
||||
return_server)
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(instance.create)()
|
||||
self.assertTrue(instance.id > 0)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_instance_validate(self):
|
||||
stack_name = 'test_instance_validate_stack'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
|
@ -879,6 +879,49 @@ class ServersTest(common.HeatTestCase):
|
||||
config_drive=None, disk_config=None, reservation_id=None,
|
||||
files={}, admin_pass='foo')
|
||||
|
||||
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)
|
||||
# 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'
|
||||
server_name = 'server_w_stack_sched_hints'
|
||||
(t, stack) = self._get_test_template(stack_name, server_name)
|
||||
|
||||
resource_defns = t.resource_definitions(stack)
|
||||
server = servers.Server(server_name,
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self._mock_get_image_id_success('CentOS 5.2', 1,
|
||||
server_rebuild=False)
|
||||
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
self.m.StubOutWithMock(self.fc.servers, 'create')
|
||||
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},
|
||||
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,
|
||||
files={}, admin_pass=None).AndReturn(return_server)
|
||||
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(server.create)()
|
||||
# this makes sure the auto increment worked on server creation
|
||||
self.assertTrue(server.id > 0)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_check_maximum(self):
|
||||
msg = 'test_check_maximum'
|
||||
self.assertIsNone(servers.Server._check_maximum(1, 1, msg))
|
||||
|
Loading…
Reference in New Issue
Block a user