Merge "Adds resource uuid, volume support to lifecycle scheduler hints"
This commit is contained in:
commit
831eaf0e7d
@ -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