Allows vip from a different subnet for lbaas resource

This fix allows user to specify a different subnet while adding
a vip to a pool resource. If vip subnet is not provided, pool
subnet is used by default.

Change-Id: I0b7740ca37961ae412866cc15ab439dafbfcf9d9
Closes-Bug: #1286001
This commit is contained in:
Rabi Mishra 2014-02-28 12:11:00 +05:30
parent b1d5a40b54
commit c2ec3291ad
2 changed files with 84 additions and 17 deletions

View File

@ -25,6 +25,7 @@ from heat.engine.resources.neutron import neutron
if clients.neutronclient is not None:
from neutronclient.common.exceptions import NeutronClientException
from neutronclient.neutron import v2_0 as neutronV20
class HealthMonitor(neutron.NeutronResource):
@ -158,11 +159,13 @@ class Pool(neutron.NeutronResource):
)
_VIP_KEYS = (
VIP_NAME, VIP_DESCRIPTION, VIP_ADDRESS, VIP_CONNECTION_LIMIT,
VIP_PROTOCOL_PORT, VIP_SESSION_PERSISTENCE, VIP_ADMIN_STATE_UP,
VIP_NAME, VIP_DESCRIPTION, VIP_SUBNET, VIP_ADDRESS,
VIP_CONNECTION_LIMIT, VIP_PROTOCOL_PORT,
VIP_SESSION_PERSISTENCE, VIP_ADMIN_STATE_UP,
) = (
'name', 'description', 'address', 'connection_limit',
'protocol_port', 'session_persistence', 'admin_state_up',
'name', 'description', 'subnet', 'address',
'connection_limit', 'protocol_port',
'session_persistence', 'admin_state_up',
)
_VIP_SESSION_PERSISTENCE_KEYS = (
@ -182,7 +185,8 @@ class Pool(neutron.NeutronResource):
),
SUBNET_ID: properties.Schema(
properties.Schema.STRING,
_('The subnet on which the members of the pool will be located.'),
_('The subnet for the port on which the members '
'of the pool will be connected.'),
required=True
),
LB_METHOD: properties.Schema(
@ -223,6 +227,10 @@ class Pool(neutron.NeutronResource):
properties.Schema.STRING,
_('Description of the vip.')
),
VIP_SUBNET: properties.Schema(
properties.Schema.STRING,
_('Subnet of the vip.')
),
VIP_ADDRESS: properties.Schema(
properties.Schema.STRING,
_('IP address of the vip.')
@ -279,8 +287,8 @@ class Pool(neutron.NeutronResource):
'admin_state_up': _('The administrative state of this pool.'),
'name': _('Name of the pool.'),
'protocol': _('Protocol to balance.'),
'subnet_id': _('The subnet on which the members of the pool '
'will be located.'),
'subnet_id': _('The subnet for the port on which the members '
'of the pool will be connected.'),
'lb_method': _('The algorithm used to distribute load between the '
'members of the pool.'),
'description': _('Description of the pool.'),
@ -331,7 +339,16 @@ class Pool(neutron.NeutronResource):
vip_arguments['session_persistence'] = prepared_props
vip_arguments['protocol'] = self.properties[self.PROTOCOL]
vip_arguments['subnet_id'] = self.properties[self.SUBNET_ID]
if vip_arguments.get(self.VIP_SUBNET) is None:
vip_arguments['subnet_id'] = self.properties[self.SUBNET_ID]
else:
vip_arguments[
'subnet_id'] = neutronV20.find_resourceid_by_name_or_id(
self.neutron(),
'subnet',
vip_arguments.pop(self.VIP_SUBNET))
vip_arguments['pool_id'] = pool['id']
vip = client.create_vip({'vip': vip_arguments})['vip']

View File

@ -13,6 +13,7 @@
# under the License.
import copy
import mox
from testtools import skipIf
@ -48,6 +49,27 @@ health_monitor_template = '''
}
'''
pool_template_with_vip_subnet = '''
{
"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,
"subnet": "sub9999"
}
}
}
}
}
'''
pool_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
@ -274,12 +296,14 @@ class PoolTest(HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client,
'disassociate_health_monitor')
self.m.StubOutWithMock(neutronclient.Client, 'create_vip')
self.m.StubOutWithMock(loadbalancer.neutronV20,
'find_resourceid_by_name_or_id')
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):
def create_pool(self, with_vip_subnet=False):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutronclient.Client.create_pool({
@ -288,18 +312,31 @@ class PoolTest(HeatTestCase):
'name': utils.PhysName('test_stack', 'pool'),
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
).AndReturn({'pool': {'id': '5678'}})
neutronclient.Client.create_vip({
stvipvsn = {
'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'}})
'admin_state_up': True, 'subnet_id': u'sub9999',
'pool_id': '5678', 'protocol_port': 80}
}
stvippsn = copy.deepcopy(stvipvsn)
stvippsn['vip']['subnet_id'] = 'sub123'
if with_vip_subnet:
neutronclient.Client.create_vip(stvipvsn
).AndReturn({'vip': {'id': 'xyz'}})
snippet = template_format.parse(pool_template_with_vip_subnet)
else:
neutronclient.Client.create_vip(stvippsn
).AndReturn({'vip': {'id': 'xyz'}})
snippet = template_format.parse(pool_template)
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)
@ -311,6 +348,19 @@ class PoolTest(HeatTestCase):
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
self.m.VerifyAll()
def test_create_with_vip_subnet(self):
loadbalancer.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub9999'
).AndReturn('sub9999')
rsrc = self.create_pool(with_vip_subnet=True)
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())
@ -472,7 +522,7 @@ class PoolTest(HeatTestCase):
pool = snippet['Resources']['pool']
persistence = pool['Properties']['vip']['session_persistence']
#When persistence type is set to APP_COOKIE, cookie_name is required
# When persistence type is set to APP_COOKIE, cookie_name is required
persistence['type'] = 'APP_COOKIE'
persistence['cookie_name'] = None
@ -515,12 +565,12 @@ class PoolTest(HeatTestCase):
pool = snippet['Resources']['pool']
persistence = pool['Properties']['vip']['session_persistence']
#change persistence type to HTTP_COOKIE that not require cookie_name
# change persistence type to HTTP_COOKIE that not require cookie_name
persistence['type'] = 'HTTP_COOKIE'
del persistence['cookie_name']
resource = loadbalancer.Pool('pool', pool, utils.parse_stack(snippet))
#assert that properties contain cookie_name property with None value
# assert that properties contain cookie_name property with None value
persistence = resource.properties['vip']['session_persistence']
self.assertIn('cookie_name', persistence)
self.assertIsNone(persistence['cookie_name'])