From a328c003e8f661f9235d2dfa37f1822b791e85f8 Mon Sep 17 00:00:00 2001
From: Thomas Herve
Date: Wed, 7 Aug 2013 13:48:04 +0200
Subject: [PATCH] Implement neutron pool resource
The branch adds a new resource creating pool instances in
Neutron LBAAS.
Change-Id: Ie55dd0cc5d2a67296ab175dc3bea11ffb63ba928
Implements: blueprint lbaas-resource
---
heat/engine/resources/neutron/loadbalancer.py | 156 +++++++-
heat/tests/test_neutron_loadbalancer.py | 367 +++++++++++++++++-
2 files changed, 512 insertions(+), 11 deletions(-)
diff --git a/heat/engine/resources/neutron/loadbalancer.py b/heat/engine/resources/neutron/loadbalancer.py
index a6dc9ffb4..51b78b3e7 100644
--- a/heat/engine/resources/neutron/loadbalancer.py
+++ b/heat/engine/resources/neutron/loadbalancer.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from heat.common import exception
from heat.engine import clients
from heat.engine import scheduler
from heat.engine.resources.neutron import neutron
@@ -44,7 +45,7 @@ class HealthMonitor(neutron.NeutronResource):
'expected_codes', 'url_path')
attributes_schema = {
- 'admin_state_up': 'the administrative state of this port',
+ 'admin_state_up': 'the administrative state of this health monitor',
'delay': 'the minimum time in seconds between regular connections '
'of the member',
'expected_codes': 'the list of HTTP status codes expected in '
@@ -75,8 +76,9 @@ class HealthMonitor(neutron.NeutronResource):
self.resource_id)['health_monitor']
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
- self.neutron().update_health_monitor(
- self.resource_id, {'health_monitor': prop_diff})
+ if prop_diff:
+ self.neutron().update_health_monitor(
+ self.resource_id, {'health_monitor': prop_diff})
def handle_delete(self):
try:
@@ -88,10 +90,158 @@ class HealthMonitor(neutron.NeutronResource):
return scheduler.TaskRunner(self._confirm_delete)()
+class Pool(neutron.NeutronResource):
+ """
+ A resource for managing load balancer pools in Neutron.
+ """
+
+ vip_schema = {
+ 'name': {'Type': 'String'},
+ 'description': {'Type': 'String'},
+ 'address': {'Type': 'String'},
+ 'connection_limit': {'Type': 'Integer'},
+ 'protocol_port': {'Type': 'Integer', 'Required': True},
+ 'admin_state_up': {'Default': True, 'Type': 'Boolean'},
+ }
+
+ properties_schema = {
+ 'protocol': {'Type': 'String', 'Required': True,
+ 'AllowedValues': ['TCP', 'HTTP', 'HTTPS']},
+ 'subnet_id': {'Type': 'String', 'Required': True},
+ 'lb_method': {'Type': 'String', 'Required': True,
+ 'AllowedValues': ['ROUND_ROBIN', 'LEAST_CONNECTIONS',
+ 'SOURCE_IP']},
+ 'name': {'Type': 'String'},
+ 'description': {'Type': 'String'},
+ 'admin_state_up': {'Default': True, 'Type': 'Boolean'},
+ 'vip': {'Type': 'Map', 'Schema': vip_schema, 'Required': True},
+ 'monitors': {'Type': 'List'},
+ }
+
+ update_allowed_keys = ('Properties',)
+ update_allowed_properties = ('description', 'admin_state_up', 'lb_method',
+ 'monitors')
+
+ attributes_schema = {
+ 'admin_state_up': 'the administrative state of this pool',
+ 'id': 'unique identifier for this pool',
+ 'name': 'friendly name of the pool',
+ 'protocol': 'protocol to balance',
+ 'subnet_id': 'the subnet on which the members of the pool '
+ 'will be located',
+ 'lb_method': 'the algorithm used to distribute load between the '
+ 'members of the pool',
+ 'description': 'description of the pool',
+ 'tenant_id': 'tenant owning the pool',
+ 'vip': 'ip of the pool',
+ }
+
+ def handle_create(self):
+ properties = self.prepare_properties(
+ self.properties,
+ self.physical_resource_name())
+ vip_properties = properties.pop('vip')
+ monitors = properties.pop('monitors', [])
+ client = self.neutron()
+ pool = client.create_pool({'pool': properties})['pool']
+ self.resource_id_set(pool['id'])
+
+ for monitor in monitors:
+ client.associate_health_monitor(
+ pool['id'], {'health_monitor': {'id': monitor}})
+
+ vip_arguments = self.prepare_properties(
+ vip_properties,
+ '%s.vip' % (self.name,))
+ vip_arguments['protocol'] = self.properties['protocol']
+ vip_arguments['subnet_id'] = self.properties['subnet_id']
+ vip_arguments['pool_id'] = pool['id']
+ vip = client.create_vip({'vip': vip_arguments})['vip']
+
+ self.metadata = {'vip': vip['id']}
+
+ def _show_resource(self):
+ return self.neutron().show_pool(self.resource_id)['pool']
+
+ def check_create_complete(self, data):
+ attributes = self._show_resource()
+ if attributes['status'] == 'PENDING_CREATE':
+ return False
+ elif attributes['status'] == 'ACTIVE':
+ vip_attributes = self.neutron().show_vip(
+ self.metadata['vip'])['vip']
+ if vip_attributes['status'] == 'PENDING_CREATE':
+ return False
+ elif vip_attributes['status'] == 'ACTIVE':
+ return True
+ raise exception.Error(
+ 'neutron reported unexpected vip resource[%s] status[%s]' %
+ (vip_attributes['name'], vip_attributes['status']))
+ raise exception.Error(
+ 'neutron report unexpected pool resource[%s] status[%s]' %
+ (attributes['name'], attributes['status']))
+
+ def handle_update(self, json_snippet, tmpl_diff, prop_diff):
+ if prop_diff:
+ client = self.neutron()
+ monitors = set(prop_diff.pop('monitors', []))
+ if monitors:
+ old_monitors = set(self.t['Properties'].get('monitors', []))
+ for monitor in old_monitors - monitors:
+ client.disassociate_health_monitor(
+ self.resource_id, {'health_monitor': {'id': monitor}})
+ for monitor in monitors - old_monitors:
+ client.associate_health_monitor(
+ self.resource_id, {'health_monitor': {'id': monitor}})
+
+ if prop_diff:
+ client.update_pool(self.resource_id, {'pool': prop_diff})
+
+ def _resolve_attribute(self, name):
+ if name == 'vip':
+ return self.neutron().show_vip(self.metadata['vip'])['vip']
+ return super(Pool, self)._resolve_attribute(name)
+
+ def _confirm_vip_delete(self):
+ client = self.neutron()
+ while True:
+ try:
+ yield
+ client.show_vip(self.metadata['vip'])
+ except NeutronClientException as ex:
+ if ex.status_code != 404:
+ raise ex
+ break
+ self._delete_pool()
+
+ def _delete_pool(self):
+ try:
+ self.neutron().delete_pool(self.resource_id)
+ except NeutronClientException as ex:
+ if ex.status_code != 404:
+ raise ex
+ else:
+ return scheduler.TaskRunner(self._confirm_delete)()
+
+ def handle_delete(self):
+ if self.metadata:
+ try:
+ self.neutron().delete_vip(self.metadata['vip'])
+ except NeutronClientException as ex:
+ if ex.status_code != 404:
+ raise ex
+ self._delete_pool()
+ else:
+ return scheduler.TaskRunner(self._confirm_vip_delete)()
+ else:
+ self._delete_pool()
+
+
def resource_mapping():
if clients.neutronclient is None:
return {}
return {
'OS::Neutron::HealthMonitor': HealthMonitor,
+ 'OS::Neutron::Pool': Pool,
}
diff --git a/heat/tests/test_neutron_loadbalancer.py b/heat/tests/test_neutron_loadbalancer.py
index a4cbbe8b4..af4409a76 100644
--- a/heat/tests/test_neutron_loadbalancer.py
+++ b/heat/tests/test_neutron_loadbalancer.py
@@ -47,6 +47,27 @@ health_monitor_template = '''
}
'''
+pool_template = '''
+{
+ "AWSTemplateFormatVersion" : "2010-09-09",
+ "Description" : "Template to test load balancer resources",
+ "Parameters" : {},
+ "Resources" : {
+ "pool": {
+ "Type": "OS::Neutron::Pool",
+ "Properties": {
+ "protocol": "HTTP",
+ "subnet_id": "sub123",
+ "lb_method": "ROUND_ROBIN",
+ "vip": {
+ "protocol_port": 80
+ }
+ }
+ }
+ }
+}
+'''
+
@skipIf(neutronclient is None, 'neutronclient unavailable')
class HealthMonitorTest(HeatTestCase):
@@ -95,13 +116,16 @@ class HealthMonitorTest(HeatTestCase):
stack = utils.parse_stack(snippet)
rsrc = loadbalancer.HealthMonitor(
'monitor', snippet['Resources']['monitor'], stack)
- self.assertRaises(exception.ResourceFailure,
- scheduler.TaskRunner(rsrc.create))
+ error = self.assertRaises(exception.ResourceFailure,
+ scheduler.TaskRunner(rsrc.create))
+ self.assertEqual(
+ 'NeutronClientException: An unknown exception occurred.',
+ str(error))
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
self.m.VerifyAll()
def test_delete(self):
- neutronclient.Client.delete_health_monitor('5678').AndReturn(None)
+ neutronclient.Client.delete_health_monitor('5678')
neutronclient.Client.show_health_monitor('5678').AndRaise(
loadbalancer.NeutronClientException(status_code=404))
@@ -130,8 +154,11 @@ class HealthMonitorTest(HeatTestCase):
rsrc = self.create_health_monitor()
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
- self.assertRaises(exception.ResourceFailure,
- scheduler.TaskRunner(rsrc.delete))
+ error = self.assertRaises(exception.ResourceFailure,
+ scheduler.TaskRunner(rsrc.delete))
+ self.assertEqual(
+ 'NeutronClientException: An unknown exception occurred.',
+ str(error))
self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
self.m.VerifyAll()
@@ -150,14 +177,17 @@ class HealthMonitorTest(HeatTestCase):
rsrc = self.create_health_monitor()
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
- self.assertRaises(exception.InvalidTemplateAttribute,
- rsrc.FnGetAtt, 'subnet_id')
+ error = self.assertRaises(exception.InvalidTemplateAttribute,
+ rsrc.FnGetAtt, 'subnet_id')
+ self.assertEqual(
+ 'The Referenced Attribute (monitor subnet_id) is incorrect.',
+ str(error))
self.m.VerifyAll()
def test_update(self):
rsrc = self.create_health_monitor()
neutronclient.Client.update_health_monitor(
- '5678', {'health_monitor': {'delay': 10}}).AndReturn(None)
+ '5678', {'health_monitor': {'delay': 10}})
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
@@ -166,3 +196,324 @@ class HealthMonitorTest(HeatTestCase):
self.assertEqual(None, rsrc.update(update_template))
self.m.VerifyAll()
+
+
+@skipIf(neutronclient is None, 'neutronclient unavailable')
+class PoolTest(HeatTestCase):
+
+ def setUp(self):
+ super(PoolTest, self).setUp()
+ self.m.StubOutWithMock(neutronclient.Client, 'create_pool')
+ self.m.StubOutWithMock(neutronclient.Client, 'delete_pool')
+ self.m.StubOutWithMock(neutronclient.Client, 'show_pool')
+ self.m.StubOutWithMock(neutronclient.Client, 'update_pool')
+ self.m.StubOutWithMock(neutronclient.Client,
+ 'associate_health_monitor')
+ self.m.StubOutWithMock(neutronclient.Client,
+ 'disassociate_health_monitor')
+ self.m.StubOutWithMock(neutronclient.Client, 'create_vip')
+ self.m.StubOutWithMock(neutronclient.Client, 'delete_vip')
+ self.m.StubOutWithMock(neutronclient.Client, 'show_vip')
+ self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
+ utils.setup_dummy_db()
+
+ def create_pool(self):
+ clients.OpenStackClients.keystone().AndReturn(
+ fakes.FakeKeystoneClient())
+ neutronclient.Client.create_pool({
+ 'pool': {
+ 'subnet_id': 'sub123', 'protocol': u'HTTP',
+ 'name': utils.PhysName('test_stack', 'pool'),
+ 'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
+ ).AndReturn({'pool': {'id': '5678'}})
+ neutronclient.Client.create_vip({
+ 'vip': {
+ 'protocol': u'HTTP', 'name': 'pool.vip',
+ 'admin_state_up': True, 'subnet_id': u'sub123',
+ 'pool_id': '5678', 'protocol_port': 80}}
+ ).AndReturn({'vip': {'id': 'xyz'}})
+ neutronclient.Client.show_pool('5678').AndReturn(
+ {'pool': {'status': 'ACTIVE'}})
+ neutronclient.Client.show_vip('xyz').AndReturn(
+ {'vip': {'status': 'ACTIVE'}})
+
+ snippet = template_format.parse(pool_template)
+ stack = utils.parse_stack(snippet)
+ return loadbalancer.Pool(
+ 'pool', snippet['Resources']['pool'], stack)
+
+ def test_create(self):
+ rsrc = self.create_pool()
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_create_pending(self):
+ clients.OpenStackClients.keystone().AndReturn(
+ fakes.FakeKeystoneClient())
+ neutronclient.Client.create_pool({
+ 'pool': {
+ 'subnet_id': 'sub123', 'protocol': u'HTTP',
+ 'name': utils.PhysName('test_stack', 'pool'),
+ 'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
+ ).AndReturn({'pool': {'id': '5678'}})
+ neutronclient.Client.create_vip({
+ 'vip': {
+ 'protocol': u'HTTP', 'name': 'pool.vip',
+ 'admin_state_up': True, 'subnet_id': u'sub123',
+ 'pool_id': '5678', 'protocol_port': 80}}
+ ).AndReturn({'vip': {'id': 'xyz'}})
+ neutronclient.Client.show_pool('5678').AndReturn(
+ {'pool': {'status': 'PENDING_CREATE'}})
+ neutronclient.Client.show_pool('5678').MultipleTimes().AndReturn(
+ {'pool': {'status': 'ACTIVE'}})
+ neutronclient.Client.show_vip('xyz').AndReturn(
+ {'vip': {'status': 'PENDING_CREATE'}})
+ neutronclient.Client.show_vip('xyz').AndReturn(
+ {'vip': {'status': 'ACTIVE'}})
+
+ snippet = template_format.parse(pool_template)
+ stack = utils.parse_stack(snippet)
+ rsrc = loadbalancer.Pool(
+ 'pool', snippet['Resources']['pool'], stack)
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_create_failed_unexpected_status(self):
+ clients.OpenStackClients.keystone().AndReturn(
+ fakes.FakeKeystoneClient())
+ neutronclient.Client.create_pool({
+ 'pool': {
+ 'subnet_id': 'sub123', 'protocol': u'HTTP',
+ 'name': utils.PhysName('test_stack', 'pool'),
+ 'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
+ ).AndReturn({'pool': {'id': '5678'}})
+ neutronclient.Client.create_vip({
+ 'vip': {
+ 'protocol': u'HTTP', 'name': 'pool.vip',
+ 'admin_state_up': True, 'subnet_id': u'sub123',
+ 'pool_id': '5678', 'protocol_port': 80}}
+ ).AndReturn({'vip': {'id': 'xyz'}})
+ neutronclient.Client.show_pool('5678').AndReturn(
+ {'pool': {'status': 'ERROR', 'name': '5678'}})
+
+ snippet = template_format.parse(pool_template)
+ stack = utils.parse_stack(snippet)
+ rsrc = loadbalancer.Pool(
+ 'pool', snippet['Resources']['pool'], stack)
+ self.m.ReplayAll()
+ error = self.assertRaises(exception.ResourceFailure,
+ scheduler.TaskRunner(rsrc.create))
+ self.assertEqual(
+ 'Error: neutron report unexpected pool '
+ 'resource[5678] status[ERROR]',
+ str(error))
+ self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_create_failed_unexpected_vip_status(self):
+ clients.OpenStackClients.keystone().AndReturn(
+ fakes.FakeKeystoneClient())
+ neutronclient.Client.create_pool({
+ 'pool': {
+ 'subnet_id': 'sub123', 'protocol': u'HTTP',
+ 'name': utils.PhysName('test_stack', 'pool'),
+ 'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
+ ).AndReturn({'pool': {'id': '5678'}})
+ neutronclient.Client.create_vip({
+ 'vip': {
+ 'protocol': u'HTTP', 'name': 'pool.vip',
+ 'admin_state_up': True, 'subnet_id': u'sub123',
+ 'pool_id': '5678', 'protocol_port': 80}}
+ ).AndReturn({'vip': {'id': 'xyz'}})
+ neutronclient.Client.show_pool('5678').MultipleTimes().AndReturn(
+ {'pool': {'status': 'ACTIVE'}})
+ neutronclient.Client.show_vip('xyz').AndReturn(
+ {'vip': {'status': 'ERROR', 'name': 'xyz'}})
+
+ snippet = template_format.parse(pool_template)
+ stack = utils.parse_stack(snippet)
+ rsrc = loadbalancer.Pool(
+ 'pool', snippet['Resources']['pool'], stack)
+ self.m.ReplayAll()
+ error = self.assertRaises(exception.ResourceFailure,
+ scheduler.TaskRunner(rsrc.create))
+ self.assertEqual(
+ 'Error: neutron reported unexpected vip '
+ 'resource[xyz] status[ERROR]',
+ str(error))
+ self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_create_failed(self):
+ clients.OpenStackClients.keystone().AndReturn(
+ fakes.FakeKeystoneClient())
+ neutronclient.Client.create_pool({
+ 'pool': {
+ 'subnet_id': 'sub123', 'protocol': u'HTTP',
+ 'name': utils.PhysName('test_stack', 'pool'),
+ 'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
+ ).AndRaise(loadbalancer.NeutronClientException())
+ self.m.ReplayAll()
+
+ snippet = template_format.parse(pool_template)
+ stack = utils.parse_stack(snippet)
+ rsrc = loadbalancer.Pool(
+ 'pool', snippet['Resources']['pool'], stack)
+ error = self.assertRaises(exception.ResourceFailure,
+ scheduler.TaskRunner(rsrc.create))
+ self.assertEqual(
+ 'NeutronClientException: An unknown exception occurred.',
+ str(error))
+ self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_delete(self):
+ rsrc = self.create_pool()
+ neutronclient.Client.delete_vip('xyz')
+ neutronclient.Client.show_vip('xyz').AndRaise(
+ loadbalancer.NeutronClientException(status_code=404))
+ neutronclient.Client.delete_pool('5678')
+ neutronclient.Client.show_pool('5678').AndRaise(
+ loadbalancer.NeutronClientException(status_code=404))
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ scheduler.TaskRunner(rsrc.delete)()
+ self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_delete_already_gone(self):
+ neutronclient.Client.delete_vip('xyz').AndRaise(
+ loadbalancer.NeutronClientException(status_code=404))
+ neutronclient.Client.delete_pool('5678').AndRaise(
+ loadbalancer.NeutronClientException(status_code=404))
+
+ rsrc = self.create_pool()
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ scheduler.TaskRunner(rsrc.delete)()
+ self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_delete_vip_failed(self):
+ neutronclient.Client.delete_vip('xyz').AndRaise(
+ loadbalancer.NeutronClientException(status_code=400))
+
+ rsrc = self.create_pool()
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ error = self.assertRaises(exception.ResourceFailure,
+ scheduler.TaskRunner(rsrc.delete))
+ self.assertEqual(
+ 'NeutronClientException: An unknown exception occurred.',
+ str(error))
+ self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_delete_failed(self):
+ neutronclient.Client.delete_vip('xyz').AndRaise(
+ loadbalancer.NeutronClientException(status_code=404))
+ neutronclient.Client.delete_pool('5678').AndRaise(
+ loadbalancer.NeutronClientException(status_code=400))
+
+ rsrc = self.create_pool()
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ error = self.assertRaises(exception.ResourceFailure,
+ scheduler.TaskRunner(rsrc.delete))
+ self.assertEqual(
+ 'NeutronClientException: An unknown exception occurred.',
+ str(error))
+ self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
+ self.m.VerifyAll()
+
+ def test_attribute(self):
+ rsrc = self.create_pool()
+ neutronclient.Client.show_pool('5678').MultipleTimes(
+ ).AndReturn(
+ {'pool': {'admin_state_up': True, 'lb_method': 'ROUND_ROBIN'}})
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ self.assertEqual(True, rsrc.FnGetAtt('admin_state_up'))
+ self.assertEqual('ROUND_ROBIN', rsrc.FnGetAtt('lb_method'))
+ self.m.VerifyAll()
+
+ def test_vip_attribute(self):
+ rsrc = self.create_pool()
+ neutronclient.Client.show_vip('xyz').AndReturn(
+ {'vip': {'address': '10.0.0.3', 'name': 'xyz'}})
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ self.assertEqual({'address': '10.0.0.3', 'name': 'xyz'},
+ rsrc.FnGetAtt('vip'))
+ self.m.VerifyAll()
+
+ def test_attribute_failed(self):
+ rsrc = self.create_pool()
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+ error = self.assertRaises(exception.InvalidTemplateAttribute,
+ rsrc.FnGetAtt, 'net_id')
+ self.assertEqual(
+ 'The Referenced Attribute (pool net_id) is incorrect.',
+ str(error))
+ self.m.VerifyAll()
+
+ def test_update(self):
+ rsrc = self.create_pool()
+ neutronclient.Client.update_pool(
+ '5678', {'pool': {'admin_state_up': False}})
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+
+ update_template = copy.deepcopy(rsrc.t)
+ update_template['Properties']['admin_state_up'] = False
+ self.assertEqual(None, rsrc.update(update_template))
+
+ self.m.VerifyAll()
+
+ def test_update_monitors(self):
+ clients.OpenStackClients.keystone().AndReturn(
+ fakes.FakeKeystoneClient())
+ neutronclient.Client.create_pool({
+ 'pool': {
+ 'subnet_id': 'sub123', 'protocol': u'HTTP',
+ 'name': utils.PhysName('test_stack', 'pool'),
+ 'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
+ ).AndReturn({'pool': {'id': '5678'}})
+ neutronclient.Client.associate_health_monitor(
+ '5678', {'health_monitor': {'id': 'mon123'}})
+ neutronclient.Client.associate_health_monitor(
+ '5678', {'health_monitor': {'id': 'mon456'}})
+ neutronclient.Client.create_vip({
+ 'vip': {
+ 'protocol': u'HTTP', 'name': 'pool.vip',
+ 'admin_state_up': True, 'subnet_id': u'sub123',
+ 'pool_id': '5678', 'protocol_port': 80}}
+ ).AndReturn({'vip': {'id': 'xyz'}})
+ neutronclient.Client.show_pool('5678').AndReturn(
+ {'pool': {'status': 'ACTIVE'}})
+ neutronclient.Client.show_vip('xyz').AndReturn(
+ {'vip': {'status': 'ACTIVE'}})
+ neutronclient.Client.disassociate_health_monitor(
+ '5678', {'health_monitor': {'id': 'mon456'}})
+ neutronclient.Client.associate_health_monitor(
+ '5678', {'health_monitor': {'id': 'mon789'}})
+
+ snippet = template_format.parse(pool_template)
+ stack = utils.parse_stack(snippet)
+ snippet['Resources']['pool']['Properties']['monitors'] = [
+ 'mon123', 'mon456']
+ rsrc = loadbalancer.Pool(
+ 'pool', snippet['Resources']['pool'], stack)
+ self.m.ReplayAll()
+ scheduler.TaskRunner(rsrc.create)()
+
+ update_template = copy.deepcopy(rsrc.t)
+ update_template['Properties']['monitors'] = ['mon123', 'mon789']
+ self.assertEqual(None, rsrc.update(update_template))
+
+ self.m.VerifyAll()
|