Add rolling update for SDG

This patch adds rolling_update/batch_create support
for SoftwareDeploymentGroup.

Change-Id: I336e66b62a7beddb306b8a61af26e12759e1b490
This commit is contained in:
Rabi Mishra 2016-06-17 17:37:53 +05:30
parent 0272577c9e
commit 5465579bdf
3 changed files with 121 additions and 4 deletions

View File

@ -390,6 +390,9 @@ class ResourceGroup(stack_resource.StackResource):
return False
return True
def res_def_changed(self, prop_diff):
return self.RESOURCE_DEF in prop_diff
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
if tmpl_diff:
# parse update policy
@ -401,7 +404,7 @@ class ResourceGroup(stack_resource.StackResource):
checkers = []
self.properties = json_snippet.properties(self.properties_schema,
self.context)
if prop_diff and self.RESOURCE_DEF in prop_diff:
if prop_diff and self.res_def_changed(prop_diff):
updaters = self._try_rolling_update()
if updaters:
checkers.extend(updaters)

View File

@ -17,6 +17,7 @@ import uuid
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import timeutils
from six import itertools
from heat.common import exception
from heat.common.i18n import _
@ -574,6 +575,14 @@ class SoftwareDeploymentGroup(resource_group.ResourceGroup):
'deploy_stdouts', 'deploy_stderrs', 'deploy_status_codes'
)
_ROLLING_UPDATES_SCHEMA_KEYS = (
MAX_BATCH_SIZE,
PAUSE_TIME,
) = (
resource_group.ResourceGroup.MAX_BATCH_SIZE,
resource_group.ResourceGroup.PAUSE_TIME,
)
_sd_ps = SoftwareDeployment.properties_schema
_rg_ps = resource_group.ResourceGroup.properties_schema
@ -611,13 +620,34 @@ class SoftwareDeploymentGroup(resource_group.ResourceGroup):
),
}
update_policy_schema = {}
rolling_update_schema = {
MAX_BATCH_SIZE: properties.Schema(
properties.Schema.INTEGER,
_('The maximum number of deployments to replace at once.'),
constraints=[constraints.Range(min=1)],
default=1),
PAUSE_TIME: properties.Schema(
properties.Schema.NUMBER,
_('The number of seconds to wait between batches of '
'updates.'),
constraints=[constraints.Range(min=0)],
default=0),
}
def get_size(self):
return len(self.properties[self.SERVERS])
def _resource_names(self):
return iter(self.properties[self.SERVERS])
def _resource_names(self, size=None):
candidates = self.properties[self.SERVERS]
if size is None:
return iter(candidates)
return itertools.islice(candidates, size)
def res_def_changed(self, prop_diff):
return True
def _name_blacklist(self):
return set()
def get_resource_def(self, include_all=False):
return dict(self.properties)
@ -646,6 +676,13 @@ class SoftwareDeploymentGroup(resource_group.ResourceGroup):
rg_attr = rg.get_attribute(rg.ATTR_ATTRIBUTES, n_attr)
return attributes.select_from_attribute(rg_attr, path)
def _try_rolling_update(self):
if self.update_policy[self.ROLLING_UPDATE]:
policy = self.update_policy[self.ROLLING_UPDATE]
return self._replace(0,
policy[self.MAX_BATCH_SIZE],
policy[self.PAUSE_TIME])
class SoftwareDeployments(SoftwareDeploymentGroup):

View File

@ -22,6 +22,7 @@ from oslo_serialization import jsonutils
from heat.common import exception as exc
from heat.common.i18n import _
from heat.common import template_format
from heat.engine.clients.os import nova
from heat.engine.clients.os import swift
from heat.engine.clients.os import zaqar
@ -1488,3 +1489,79 @@ class SoftwareDeploymentGroupTest(common.HeatTestCase):
snip = stack.t.resource_definitions(stack)['deploy_mysql']
resg = sd.SoftwareDeploymentGroup('deploy_mysql', snip, stack)
self.assertIsNone(resg.validate())
class SDGReplaceTest(common.HeatTestCase):
template = {
'heat_template_version': '2013-05-23',
'resources': {
'deploy_mysql': {
'type': 'OS::Heat::SoftwareDeploymentGroup',
'properties': {
'config': 'config_uuid',
'servers': {'server1': 'uuid1', 'server2': 'uuid2'},
'input_values': {'foo': 'bar'},
'name': '10_config'
}
}
}
}
# 1. existing > batch_size
# 2. existing < batch_size
# 3. count > existing
# 4. count < exiting
# 5. with pause_sec
scenarios = [
('1', dict(count=2,
existing=['0', '1'], batch_size=1,
pause_sec=0, tasks=2)),
('2', dict(count=4,
existing=['0', '1'], batch_size=3,
pause_sec=0, tasks=2)),
('3', dict(count=3,
existing=['0', '1'], batch_size=2,
pause_sec=0, tasks=2)),
('4', dict(count=2,
existing=['0', '1', '2'], batch_size=2,
pause_sec=0, tasks=1)),
('5', dict(count=2,
existing=['0', '1'], batch_size=1,
pause_sec=1, tasks=3))]
def get_fake_nested_stack(self, names):
nested_t = '''
heat_template_version: 2015-04-30
description: Resource Group
resources:
'''
resource_snip = '''
'%s':
type: SoftwareDeployment
properties:
foo: bar
'''
resources = [nested_t]
for res_name in names:
resources.extend([resource_snip % res_name])
nested_t = ''.join(resources)
return utils.parse_stack(template_format.parse(nested_t))
def setUp(self):
super(SDGReplaceTest, self).setUp()
self.stack = utils.parse_stack(self.template)
snip = self.stack.t.resource_definitions(self.stack)['deploy_mysql']
self.group = sd.SoftwareDeploymentGroup('deploy_mysql',
snip, self.stack)
self.group.update_with_template = mock.Mock()
self.group.check_update_complete = mock.Mock()
def test_rolling_updates(self):
self.group._nested = self.get_fake_nested_stack(self.existing)
self.group.get_size = mock.Mock(return_value=self.count)
tasks = self.group._replace(0, self.batch_size,
self.pause_sec)
self.assertEqual(self.tasks,
len(tasks))