diff options
author | Serg Melikyan <smelikyan@mirantis.com> | 2013-12-10 14:51:36 +0400 |
---|---|---|
committer | Serg Melikyan <smelikyan@mirantis.com> | 2013-12-23 17:50:57 +0400 |
commit | af064c7a5304c672f7d71e4c8df5f69c195c739a (patch) | |
tree | 140db0573915caf08aaee124235fc2b28f0c4e2d /heat | |
parent | 0cd7bb64c44b19132c5cb6d43f1629602c819271 (diff) | |
download | heat-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.py | 46 | ||||
-rw-r--r-- | heat/tests/test_neutron_loadbalancer.py | 76 |
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') |