Move LB reload logic into scaling library

The lb_reload() logic is moved into a separate module of the scaling
library.  A new test case is added to test this utility function.

Since the revised _lb_reload() method checks if LOAD_BALANCER_NAMES is
defined/provided or not, the OpenStack version of AutoScalingGroup
doesn't have to implement an empty _lb_reload() method now.

partial-blueprint: as-lib
partial-blueprint: reorg-asg-code

Change-Id: I7387d0f27c6121be29d9a035be4cca0b9bbdf441
This commit is contained in:
tengqm 2014-12-23 14:20:49 +08:00
parent 1bc2e6c35c
commit f433cfc73e
4 changed files with 198 additions and 40 deletions

View File

@ -11,10 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from heat.common import environment_format
from heat.common import exception
from heat.common import grouputils
from heat.common.i18n import _
from heat.common import timeutils as iso8601utils
@ -24,6 +21,7 @@ from heat.engine import properties
from heat.engine import rsrc_defn
from heat.engine import scheduler
from heat.engine import stack_resource
from heat.scaling import lbutils
from heat.scaling import template
@ -348,37 +346,10 @@ class InstanceGroup(stack_resource.StackResource):
self._lb_reload()
def _lb_reload(self, exclude=None):
'''
Notify the LoadBalancer to reload its config to include
the changes in instances we have just made.
This must be done after activation (instance in ACTIVE state),
otherwise the instances' IP addresses may not be available.
'''
exclude = exclude or []
if self.properties[self.LOAD_BALANCER_NAMES]:
id_list = grouputils.get_member_refids(self, exclude=exclude)
for lb in self.properties[self.LOAD_BALANCER_NAMES]:
lb_resource = self.stack[lb]
props = copy.copy(lb_resource.properties.data)
if 'Instances' in lb_resource.properties_schema:
props['Instances'] = id_list
elif 'members' in lb_resource.properties_schema:
props['members'] = id_list
else:
raise exception.Error(
_("Unsupported resource '%s' in LoadBalancerNames") %
(lb,))
lb_defn = rsrc_defn.ResourceDefinition(
lb_resource.name,
lb_resource.type(),
properties=props,
metadata=lb_resource.t.get('Metadata'),
deletion_policy=lb_resource.t.get('DeletionPolicy'))
scheduler.TaskRunner(lb_resource.update, lb_defn)()
lb_names = self.properties.get(self.LOAD_BALANCER_NAMES, None)
if lb_names:
lb_dict = dict((name, self.stack[name]) for name in lb_names)
lbutils.reload_loadbalancers(self, lb_dict, exclude)
def FnGetRefId(self):
return self.physical_resource_name_or_FnGetRefId()

View File

@ -125,12 +125,6 @@ class AutoScalingResourceGroup(aws_asg.AutoScalingGroup):
properties=rsrc.get('properties'),
metadata=rsrc.get('metadata'))
def _lb_reload(self, exclude=None):
"""AutoScalingResourceGroup does not maintain load balancer
connections, so we just ignore calls to update the LB.
"""
pass
def _try_rolling_update(self, prop_diff):
if (self.properties[self.ROLLING_UPDATES] and
self.RESOURCE in prop_diff):

51
heat/scaling/lbutils.py Normal file
View File

@ -0,0 +1,51 @@
#
# 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.
import copy
import six
from heat.common import exception
from heat.common import grouputils
from heat.common.i18n import _
from heat.engine import rsrc_defn
from heat.engine import scheduler
def reload_loadbalancers(group, load_balancers, exclude=None):
'''
Notify the LoadBalancer to reload its config.
This must be done after activation (instance in ACTIVE state), otherwise
the instances' IP addresses may not be available.
'''
exclude = exclude or []
id_list = grouputils.get_member_refids(group, exclude=exclude)
for name, lb in six.iteritems(load_balancers):
props = copy.copy(lb.properties.data)
if 'Instances' in lb.properties_schema:
props['Instances'] = id_list
elif 'members' in lb.properties_schema:
props['members'] = id_list
else:
raise exception.Error(
_("Unsupported resource '%s' in LoadBalancerNames") % name)
lb_defn = rsrc_defn.ResourceDefinition(
lb.name,
lb.type(),
properties=props,
metadata=lb.t.get('Metadata'),
deletion_policy=lb.t.get('DeletionPolicy'))
scheduler.TaskRunner(lb.update, lb_defn)()

View File

@ -0,0 +1,142 @@
#
# 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.
import mock
import six
from heat.common import exception
from heat.common import grouputils
from heat.common import template_format
from heat.engine import properties
from heat.engine import resource
from heat.scaling import lbutils
from heat.tests import common
from heat.tests import generic_resource
from heat.tests import utils
lb_stack = '''
heat_template_version: 2013-05-23
resources:
neutron_lb_1:
type: Mock::Neutron::LB
neutron_lb_2:
type: Mock::Neutron::LB
aws_lb_1:
type: Mock::AWS::LB
aws_lb_2:
type: Mock::AWS::LB
non_lb:
type: Mock::Not::LB
'''
class MockedNeutronLB(generic_resource.GenericResource):
properties_schema = {
'members': properties.Schema(
properties.Schema.LIST,
update_allowed=True)
}
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
return
class MockedAWSLB(generic_resource.GenericResource):
properties_schema = {
'Instances': properties.Schema(
properties.Schema.LIST,
update_allowed=True)
}
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
return
class LBUtilsTest(common.HeatTestCase):
# need to mock a group so that group_utils.get_member_refids will work
# load_balancers is a dict of load_balancer objects, where each lb has
# properties for checking
def setUp(self):
super(LBUtilsTest, self).setUp()
resource._register_class('Mock::Neutron::LB', MockedNeutronLB)
resource._register_class('Mock::AWS::LB', MockedAWSLB)
resource._register_class('Mock::Not::LB',
generic_resource.GenericResource)
t = template_format.parse(lb_stack)
self.stack = utils.parse_stack(t)
def test_reload_aws_lb(self):
group = mock.Mock()
self.patchobject(grouputils, 'get_member_refids',
return_value=['ID1', 'ID2', 'ID3'])
lb1 = self.stack['aws_lb_1']
lb2 = self.stack['aws_lb_2']
lbs = {
'LB_1': lb1,
'LB_2': lb2
}
lb1.handle_update = mock.Mock()
lb2.handle_update = mock.Mock()
prop_diff = {'Instances': ['ID1', 'ID2', 'ID3']}
lbutils.reload_loadbalancers(group, lbs)
# For verification's purpose, we just check the prop_diff
lb1.handle_update.assert_called_with(mock.ANY, mock.ANY,
prop_diff)
lb2.handle_update.assert_called_with(mock.ANY, mock.ANY,
prop_diff)
def test_reload_neutron_lb(self):
group = mock.Mock()
self.patchobject(grouputils, 'get_member_refids',
return_value=['ID1', 'ID2', 'ID3'])
lb1 = self.stack['neutron_lb_1']
lb2 = self.stack['neutron_lb_2']
lbs = {
'LB_1': lb1,
'LB_2': lb2
}
lb1.handle_update = mock.Mock()
lb2.handle_update = mock.Mock()
prop_diff = {'members': ['ID1', 'ID2', 'ID3']}
lbutils.reload_loadbalancers(group, lbs)
# For verification's purpose, we just check the prop_diff
lb1.handle_update.assert_called_with(mock.ANY, mock.ANY,
prop_diff)
lb2.handle_update.assert_called_with(mock.ANY, mock.ANY,
prop_diff)
def test_reload_non_lb(self):
group = mock.Mock()
self.patchobject(grouputils, 'get_member_refids',
return_value=['ID1', 'ID2', 'ID3'])
lbs = {
'LB_1': self.stack['non_lb'],
}
error = self.assertRaises(exception.Error,
lbutils.reload_loadbalancers,
group, lbs)
self.assertIn("Unsupported resource 'LB_1' in LoadBalancerNames",
six.text_type(error))