summaryrefslogtreecommitdiff
path: root/heat
diff options
context:
space:
mode:
authorSerg Melikyan <smelikyan@mirantis.com>2013-12-10 14:51:36 +0400
committerSerg Melikyan <smelikyan@mirantis.com>2013-12-23 17:50:57 +0400
commitaf064c7a5304c672f7d71e4c8df5f69c195c739a (patch)
tree140db0573915caf08aaee124235fc2b28f0c4e2d /heat
parent0cd7bb64c44b19132c5cb6d43f1629602c819271 (diff)
downloadheat-af064c7a5304c672f7d71e4c8df5f69c195c739a.tar.gz
Added session_persistence property to VIP
Added new property session_persistance to the OS::Neutron::LoadBalancer resource attribute VIP. Now user can configure session stickness feature in Neutron LBaaS. Change-Id: I9afd1a8c222110899a90a940e7a549278c4467b7 Closes-Bug: #1259078
Diffstat (limited to 'heat')
-rw-r--r--heat/engine/resources/neutron/loadbalancer.py46
-rw-r--r--heat/tests/test_neutron_loadbalancer.py76
2 files changed, 119 insertions, 3 deletions
diff --git a/heat/engine/resources/neutron/loadbalancer.py b/heat/engine/resources/neutron/loadbalancer.py
index 98b0e3372..fba8edb26 100644
--- a/heat/engine/resources/neutron/loadbalancer.py
+++ b/heat/engine/resources/neutron/loadbalancer.py
@@ -160,10 +160,16 @@ class Pool(neutron.NeutronResource):
_VIP_KEYS = (
VIP_NAME, VIP_DESCRIPTION, VIP_ADDRESS, VIP_CONNECTION_LIMIT,
- VIP_PROTOCOL_PORT, VIP_ADMIN_STATE_UP,
+ VIP_PROTOCOL_PORT, VIP_SESSION_PERSISTENCE, VIP_ADMIN_STATE_UP,
) = (
'name', 'description', 'address', 'connection_limit',
- 'protocol_port', 'admin_state_up',
+ 'protocol_port', 'session_persistence', 'admin_state_up',
+ )
+
+ _VIP_SESSION_PERSISTENCE_KEYS = (
+ VIP_SESSION_PERSISTENCE_TYPE, VIP_SESSION_PERSISTENCE_COOKIE_NAME,
+ ) = (
+ 'type', 'cookie_name',
)
properties_schema = {
@@ -233,6 +239,26 @@ class Pool(neutron.NeutronResource):
'that is associated with the vip address.'),
required=True
),
+ VIP_SESSION_PERSISTENCE: properties.Schema(
+ properties.Schema.MAP,
+ _('Configuration of session persistence.'),
+ schema={
+ VIP_SESSION_PERSISTENCE_TYPE: properties.Schema(
+ properties.Schema.STRING,
+ _('Method of implementation of session '
+ 'persistence feature.'),
+ required=True,
+ constraints=[constraints.AllowedValues(
+ ['SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE']
+ )]
+ ),
+ VIP_SESSION_PERSISTENCE_COOKIE_NAME: properties.Schema(
+ properties.Schema.STRING,
+ _('Name of the cookie, '
+ 'required if type is APP_COOKIE.')
+ )
+ }
+ ),
VIP_ADMIN_STATE_UP: properties.Schema(
properties.Schema.BOOLEAN,
_('The administrative state of this vip.'),
@@ -263,6 +289,22 @@ class Pool(neutron.NeutronResource):
'vip': _('Vip associated with the pool.'),
}
+ def validate(self):
+ res = super(Pool, self).validate()
+ if res:
+ return res
+
+ session_p = self.properties[self.VIP].get(self.VIP_SESSION_PERSISTENCE)
+ persistence_type = session_p[self.VIP_SESSION_PERSISTENCE_TYPE]
+
+ if session_p is not None and persistence_type == 'APP_COOKIE':
+ if session_p.get(self.VIP_SESSION_PERSISTENCE_COOKIE_NAME):
+ return
+
+ msg = _('Property cookie_name is required, when '
+ 'session_persistence type is set to APP_COOKIE.')
+ raise exception.StackValidationFailed(message=msg)
+
def handle_create(self):
properties = self.prepare_properties(
self.properties,
diff --git a/heat/tests/test_neutron_loadbalancer.py b/heat/tests/test_neutron_loadbalancer.py
index d91b6210e..abf537b75 100644
--- a/heat/tests/test_neutron_loadbalancer.py
+++ b/heat/tests/test_neutron_loadbalancer.py
@@ -61,7 +61,7 @@ pool_template = '''
"subnet_id": "sub123",
"lb_method": "ROUND_ROBIN",
"vip": {
- "protocol_port": 80
+ "protocol_port": 80
}
}
}
@@ -105,6 +105,32 @@ lb_template = '''
'''
+pool_with_session_persistence_template = '''
+{
+ "AWSTemplateFormatVersion" : "2010-09-09",
+ "Description" : "Template to test load balancer resources wit",
+ "Parameters" : {},
+ "Resources" : {
+ "pool": {
+ "Type": "OS::Neutron::Pool",
+ "Properties": {
+ "protocol": "HTTP",
+ "subnet_id": "sub123",
+ "lb_method": "ROUND_ROBIN",
+ "vip": {
+ "protocol_port": 80,
+ "session_persistence": {
+ "type": "APP_COOKIE",
+ "cookie_name": "cookie"
+ }
+ }
+ }
+ }
+ }
+}
+'''
+
+
@skipIf(neutronclient is None, 'neutronclient unavailable')
class HealthMonitorTest(HeatTestCase):
@@ -407,6 +433,54 @@ class PoolTest(HeatTestCase):
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
self.m.VerifyAll()
+ def test_create_with_session_persistence(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,
+ 'session_persistence': {'type': 'APP_COOKIE',
+ 'cookie_name': 'cookie'}}}
+ ).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_with_session_persistence_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_failing_validation_with_session_persistence(self):
+ msg = _('Property cookie_name is required, when '
+ 'session_persistence type is set to APP_COOKIE.')
+ snippet = template_format.parse(pool_with_session_persistence_template)
+ pool = snippet['Resources']['pool']
+ persistence = pool['Properties']['vip']['session_persistence']
+
+ #When persistence type is set to APP_COOKIE, cookie_name is required
+ persistence['type'] = 'APP_COOKIE'
+ persistence['cookie_name'] = None
+
+ resource = loadbalancer.Pool('pool', pool, utils.parse_stack(snippet))
+
+ error = self.assertRaises(exception.StackValidationFailed,
+ resource.validate)
+ self.assertEqual(msg, str(error))
+
def test_delete(self):
rsrc = self.create_pool()
neutronclient.Client.delete_vip('xyz')