neutron-lbaas/neutron_lbaas/tests/unit/drivers/octavia/test_octavia_driver.py

627 lines
23 KiB
Python

# Copyright 2015, Banashankar Veerad, Copyright IBM Corporation
#
# 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 mock
from oslo_config import cfg
from neutron import context
from neutron_lbaas.drivers.octavia import driver
from neutron_lbaas.services.loadbalancer import constants
from neutron_lbaas.services.loadbalancer import data_models
from neutron_lbaas.tests.unit.db.loadbalancer import test_db_loadbalancerv2
class ManagerTest(object):
def __init__(self, parent, manager, mocked_req):
self.parent = parent
self.context = parent.context
self.driver = parent.driver
self.manager = manager
self.mocked_req = mocked_req
def create(self, model, url, args):
self.manager.create(self.context, model)
self.mocked_req.post.assert_called_with(url, args)
def update(self, old_model, model, url, args):
self.manager.update(self.context, old_model, model)
self.mocked_req.put.assert_called_with(url, args)
def delete(self, model, url):
self.manager.delete(self.context, model)
self.mocked_req.delete.assert_called_with(url)
def delete_cascade(self, model, url):
self.manager.delete_cascade(self.context, model)
self.mocked_req.delete.assert_called_with(url)
# TODO(Banashankar) : Complete refresh function. Need more info.
def refresh(self):
pass
# TODO(Banashankar): Complete stats function. Need more info.
def stats(self):
pass
class BaseOctaviaDriverTest(test_db_loadbalancerv2.LbaasPluginDbTestCase):
# Copied it from Brocade's test code :/
def _create_fake_models(self, children=True, graph=False):
# This id is used for all the entities.
id = 'test_id'
lb = data_models.LoadBalancer(id=id)
if not children:
return lb
sni_container = data_models.SNI(listener_id=id)
listener = data_models.Listener(id=id, loadbalancer=lb,
sni_containers=[sni_container])
pool = data_models.Pool(id=id, loadbalancer=lb)
member = data_models.Member(id=id, pool=pool)
hm = data_models.HealthMonitor(id=id, pool=pool)
sp = data_models.SessionPersistence(pool_id=pool.id, pool=pool)
l7policy = data_models.L7Policy(
id=id, listener=listener,
action=constants.L7_POLICY_ACTION_REDIRECT_TO_POOL)
l7rule = data_models.L7Rule(
id=id, policy=l7policy, type=constants.L7_RULE_TYPE_PATH,
compare_type=constants.L7_RULE_COMPARE_TYPE_STARTS_WITH,
value='/api')
lb.listeners = [listener]
lb.pools = [pool]
if graph:
r_pool = data_models.Pool(id=id, loadbalancer=lb)
r_member = data_models.Member(id=id, pool=r_pool)
r_pool.members = [r_member]
l7policy.redirect_pool = r_pool
l7policy.redirect_pool_id = r_pool.id
lb.pools.append(r_pool)
else:
l7policy.redirect_pool = pool
l7policy.redirect_pool_id = pool.id
listener.default_pool = pool
listener.l7_policies = [l7policy]
l7policy.rules = [l7rule]
pool.members = [member]
pool.session_persistence = sp
pool.healthmonitor = hm
return lb
def setUp(self):
super(BaseOctaviaDriverTest, self).setUp()
self.context = context.get_admin_context()
self.plugin = mock.Mock()
self.driver = driver.OctaviaDriver(self.plugin)
# mock of rest call.
self.driver.req = mock.Mock()
self.lb = self._create_fake_models()
class TestOctaviaDriver(BaseOctaviaDriverTest):
def test_allocates_vip(self):
self.addCleanup(cfg.CONF.clear_override,
'allocates_vip', group='octavia')
cfg.CONF.set_override('allocates_vip', True, group='octavia')
test_driver = driver.OctaviaDriver(self.plugin)
self.assertTrue(test_driver.load_balancer.allocates_vip)
def test_create_load_balancer_graph(self):
m = ManagerTest(self, self.driver.load_balancer,
self.driver.req)
lb = self._create_fake_models(children=True, graph=True)
listener = lb.listeners[0]
sni_container = listener.sni_containers[0]
policy = listener.l7_policies[0]
r_pool = policy.redirect_pool
r_member = r_pool.members[0]
rule = policy.rules[0]
pool = listener.default_pool
member = pool.members[0]
sp = pool.session_persistence
hm = pool.healthmonitor
lb_url = '/v1/loadbalancers'
args = {
"id": lb.id,
"name": lb.name,
"enabled": lb.admin_state_up,
"vip": {
"subnet_id": lb.vip_subnet_id,
"port_id": lb.vip_port_id,
"ip_address": lb.vip_address
},
"listeners": [{
"id": listener.id,
"protocol": listener.protocol,
"enabled": listener.admin_state_up,
"sni_containers": [sni_container.tls_container_id],
"tls_certificate_id": listener.default_tls_container_id,
"l7policies": [{
"id": policy.id,
"name": policy.name,
"redirect_pool": {
"id": r_pool.id,
"lb_algorithm": r_pool.lb_algorithm,
"protocol": r_pool.protocol,
"name": r_pool.name,
"enabled": r_pool.admin_state_up,
"session_persistence": r_pool.session_persistence,
"members": [{
"project_id": r_member.tenant_id,
"weight": r_member.weight,
"subnet_id": r_member.subnet_id,
"ip_address": r_member.address,
"protocol_port": r_member.protocol_port,
"enabled": r_member.admin_state_up,
"id": r_member.id
}],
"project_id": r_pool.tenant_id,
"description": r_pool.description
},
"l7rules": [{
"id": rule.id,
"type": rule.type,
"compare_type": rule.compare_type,
"key": rule.key,
"value": rule.value,
"invert": rule.invert
}],
"enabled": policy.admin_state_up,
"action": policy.action,
"position": policy.position,
"description": policy.description
}],
"name": listener.name,
"description": listener.description,
"default_pool": {
"id": pool.id,
"lb_algorithm": pool.lb_algorithm,
"protocol": pool.protocol,
"name": pool.name,
"enabled": pool.admin_state_up,
"session_persistence": {
"cookie_name": sp.cookie_name,
"type": sp.type
},
"members": [{
"project_id": member.tenant_id,
"weight": member.weight,
"subnet_id": member.subnet_id,
"ip_address": member.address,
"protocol_port": member.protocol_port,
"enabled": member.admin_state_up,
"id": member.id
}],
"health_monitor": {
'type': hm.type,
'delay': hm.delay,
'timeout': hm.timeout,
'rise_threshold': hm.max_retries,
'fall_threshold': hm.max_retries,
'http_method': hm.http_method,
'url_path': hm.url_path,
'expected_codes': hm.expected_codes,
'enabled': hm.admin_state_up,
'project_id': hm.tenant_id
},
"project_id": pool.tenant_id,
"description": pool.description
},
"connection_limit": listener.connection_limit,
"protocol_port": listener.protocol_port,
"project_id": listener.tenant_id
}],
"project_id": lb.tenant_id,
"description": lb.description
}
m.create(lb, lb_url, args)
def test_load_balancer_ops(self):
m = ManagerTest(self, self.driver.load_balancer,
self.driver.req)
lb = self._create_fake_models(children=False)
# urls for assert test.
lb_url = '/v1/loadbalancers'
lb_url_id = '/v1/loadbalancers/' + lb.id
# Create LB test
# args for create assert.
args = {
'id': lb.id,
'name': lb.name,
'description': lb.description,
'enabled': lb.admin_state_up,
'project_id': lb.tenant_id,
'vip': {
'subnet_id': lb.vip_subnet_id,
'ip_address': lb.vip_address,
'port_id': lb.vip_port_id,
}
}
m.create(lb, lb_url, args)
# Update LB test
# args for update assert.
args = {
'name': lb.name,
'description': lb.description,
'enabled': lb.admin_state_up,
}
m.update(lb, lb, lb_url_id, args)
# delete LB test
m.delete_cascade(lb, lb_url_id + '/delete_cascade')
# TODO(Banashankar) : refresh n stats fucntions are not yet done.
#m.refresh()
#m.stats()
def test_listener_ops(self):
m = ManagerTest(self, self.driver.listener,
self.driver.req)
listener = self.lb.listeners[0]
# urls for assert test.
list_url = '/v1/loadbalancers/%s/listeners' % listener.loadbalancer.id
list_url_id = list_url + '/%s' % (listener.id)
# Create Listener test.
# args for create and update assert.
sni_containers = [sni.tls_container_id
for sni in listener.sni_containers]
args = {
'id': listener.id,
'name': listener.name,
'description': listener.description,
'enabled': listener.admin_state_up,
'protocol': listener.protocol,
'protocol_port': listener.protocol_port,
'connection_limit': listener.connection_limit,
'tls_certificate_id': listener.default_tls_container_id,
'sni_containers': sni_containers,
'default_pool_id': listener.default_pool_id,
'project_id': listener.tenant_id
}
m.create(listener, list_url, args)
# Update listener test.
del args['id']
del args['project_id']
m.update(listener, listener, list_url_id, args)
# Delete listener.
m.delete(listener, list_url_id)
def test_pool_ops(self):
m = ManagerTest(self, self.driver.pool,
self.driver.req)
pool = self.lb.listeners[0].default_pool
# urls for assert test.
pool_url = '/v1/loadbalancers/%s/pools' % (
pool.loadbalancer.id)
pool_url_id = pool_url + "/%s" % pool.id
# Test create pool.
# args for create and update assert.
args = {
'id': pool.id,
'name': pool.name,
'description': pool.description,
'enabled': pool.admin_state_up,
'protocol': pool.protocol,
'lb_algorithm': pool.lb_algorithm,
'project_id': pool.tenant_id
}
if pool.session_persistence:
args['session_persistence'] = {
'type': pool.session_persistence.type,
'cookie_name': pool.session_persistence.cookie_name,
}
else:
args['session_persistence'] = None
m.create(pool, pool_url, args)
# Test update pool.
del args['id']
del args['project_id']
m.update(pool, pool, pool_url_id, args)
# Test pool delete.
m.delete(pool, pool_url_id)
def test_member_ops(self):
m = ManagerTest(self, self.driver.member,
self.driver.req)
member = self.lb.listeners[0].default_pool.members[0]
# urls for assert.
mem_url = '/v1/loadbalancers/%s/pools/%s/members' % (
member.pool.loadbalancer.id,
member.pool.id)
mem_url_id = mem_url + "/%s" % member.id
# Test Create member.
# args for create assert.
args = {
'id': member.id,
'enabled': member.admin_state_up,
'ip_address': member.address,
'protocol_port': member.protocol_port,
'weight': member.weight,
'subnet_id': member.subnet_id,
'project_id': member.tenant_id
}
m.create(member, mem_url, args)
# Test member update.
# args for update assert.
args = {
'enabled': member.admin_state_up,
'protocol_port': member.protocol_port,
'weight': member.weight,
}
m.update(member, member, mem_url_id, args)
# Test member delete.
m.delete(member, mem_url_id)
def test_health_monitor_ops(self):
m = ManagerTest(self, self.driver.health_monitor,
self.driver.req)
hm = self.lb.listeners[0].default_pool.healthmonitor
# urls for assert.
hm_url = '/v1/loadbalancers/%s/pools/%s/healthmonitor' % (
hm.pool.loadbalancer.id,
hm.pool.id)
# Test HM create.
# args for create and update assert.
args = {
'type': hm.type,
'delay': hm.delay,
'timeout': hm.timeout,
'rise_threshold': hm.max_retries,
'fall_threshold': hm.max_retries,
'http_method': hm.http_method,
'url_path': hm.url_path,
'expected_codes': hm.expected_codes,
'enabled': hm.admin_state_up,
'project_id': hm.tenant_id
}
m.create(hm, hm_url, args)
# Test HM update
del args['project_id']
m.update(hm, hm, hm_url, args)
# Test HM delete
m.delete(hm, hm_url)
def test_l7_policy_ops_reject(self):
m = ManagerTest(self, self.driver.l7policy,
self.driver.req)
l7p = copy.deepcopy(self.lb.listeners[0].l7_policies[0])
l7p.action = constants.L7_POLICY_ACTION_REJECT
# urls for assert.
l7p_url = '/v1/loadbalancers/%s/listeners/%s/l7policies' % (
l7p.listener.loadbalancer.id,
l7p.listener.id)
l7p_url_id = l7p_url + "/%s" % l7p.id
# Test L7Policy create.
# args for create and update assert.
args = {
'id': l7p.id,
'name': l7p.name,
'description': l7p.description,
'action': constants.L7_POLICY_ACTION_REJECT,
'position': l7p.position,
'enabled': l7p.admin_state_up
}
m.create(l7p, l7p_url, args)
# Test L7Policy update
del args['id']
m.update(l7p, l7p, l7p_url_id, args)
# Test L7Policy delete
m.delete(l7p, l7p_url_id)
def test_l7_policy_ops_rdr_pool(self):
m = ManagerTest(self, self.driver.l7policy,
self.driver.req)
l7p = copy.deepcopy(self.lb.listeners[0].l7_policies[0])
l7p.action = constants.L7_POLICY_ACTION_REDIRECT_TO_POOL
# urls for assert.
l7p_url = '/v1/loadbalancers/%s/listeners/%s/l7policies' % (
l7p.listener.loadbalancer.id,
l7p.listener.id)
l7p_url_id = l7p_url + "/%s" % l7p.id
# Test L7Policy create.
# args for create and update assert.
args = {
'id': l7p.id,
'name': l7p.name,
'description': l7p.description,
'action': constants.L7_POLICY_ACTION_REDIRECT_TO_POOL,
'redirect_pool_id': l7p.redirect_pool_id,
'position': l7p.position,
'enabled': l7p.admin_state_up
}
m.create(l7p, l7p_url, args)
# Test L7Policy update
del args['id']
m.update(l7p, l7p, l7p_url_id, args)
# Test L7Policy delete
m.delete(l7p, l7p_url_id)
def test_l7_policy_ops_rdr_url(self):
m = ManagerTest(self, self.driver.l7policy,
self.driver.req)
l7p = copy.deepcopy(self.lb.listeners[0].l7_policies[0])
l7p.action = constants.L7_POLICY_ACTION_REDIRECT_TO_URL
# urls for assert.
l7p_url = '/v1/loadbalancers/%s/listeners/%s/l7policies' % (
l7p.listener.loadbalancer.id,
l7p.listener.id)
l7p_url_id = l7p_url + "/%s" % l7p.id
# Test L7Policy create.
# args for create and update assert.
args = {
'id': l7p.id,
'name': l7p.name,
'description': l7p.description,
'action': constants.L7_POLICY_ACTION_REDIRECT_TO_URL,
'redirect_url': l7p.redirect_url,
'position': l7p.position,
'enabled': l7p.admin_state_up
}
m.create(l7p, l7p_url, args)
# Test L7Policy update
del args['id']
m.update(l7p, l7p, l7p_url_id, args)
# Test L7Policy delete
m.delete(l7p, l7p_url_id)
def test_l7_rule_ops(self):
m = ManagerTest(self, self.driver.l7rule,
self.driver.req)
l7r = self.lb.listeners[0].l7_policies[0].rules[0]
# urls for assert.
l7r_url = '/v1/loadbalancers/%s/listeners/%s/l7policies/%s/l7rules' % (
l7r.policy.listener.loadbalancer.id,
l7r.policy.listener.id,
l7r.policy.id)
l7r_url_id = l7r_url + "/%s" % l7r.id
# Test L7Rule create.
# args for create and update assert.
args = {
'id': l7r.id,
'type': l7r.type,
'compare_type': l7r.compare_type,
'key': l7r.key,
'value': l7r.value,
'invert': l7r.invert
}
m.create(l7r, l7r_url, args)
# Test L7rule update
del args['id']
m.update(l7r, l7r, l7r_url_id, args)
# Test L7Rule delete
m.delete(l7r, l7r_url_id)
class TestThreadedDriver(BaseOctaviaDriverTest):
def setUp(self):
super(TestThreadedDriver, self).setUp()
cfg.CONF.set_override('request_poll_interval', 1, group='octavia')
cfg.CONF.set_override('request_poll_timeout', 5, group='octavia')
self.driver.req.get = mock.MagicMock()
self.succ_completion = mock.MagicMock()
self.fail_completion = mock.MagicMock()
self.context = mock.MagicMock()
ctx_patcher = mock.patch('neutron.context.get_admin_context',
return_value=self.context)
ctx_patcher.start()
self.addCleanup(ctx_patcher.stop)
self.driver.load_balancer.successful_completion = (
self.succ_completion)
self.driver.load_balancer.failed_completion = self.fail_completion
def test_thread_op_goes_active(self):
self.driver.req.get.side_effect = [
{'provisioning_status': 'PENDING_CREATE'},
{'provisioning_status': 'ACTIVE'}
]
driver.thread_op(self.driver.load_balancer, self.lb)
self.succ_completion.assert_called_once_with(self.context, self.lb,
delete=False)
self.assertEqual(0, self.fail_completion.call_count)
def test_thread_op_goes_deleted(self):
self.driver.req.get.side_effect = [
{'provisioning_status': 'PENDING_DELETE'},
{'provisioning_status': 'DELETED'}
]
driver.thread_op(self.driver.load_balancer, self.lb, delete=True)
self.succ_completion.assert_called_once_with(self.context, self.lb,
delete=True)
self.assertEqual(0, self.fail_completion.call_count)
def test_thread_op_goes_error(self):
self.driver.req.get.side_effect = [
{'provisioning_status': 'PENDING_CREATE'},
{'provisioning_status': 'ERROR'}
]
driver.thread_op(self.driver.load_balancer, self.lb)
self.fail_completion.assert_called_once_with(self.context, self.lb)
self.assertEqual(0, self.succ_completion.call_count)
def test_thread_op_a_times_out(self):
cfg.CONF.set_override('request_poll_timeout', 1, group='octavia')
self.driver.req.get.side_effect = [
{'provisioning_status': 'PENDING_CREATE'}
]
driver.thread_op(self.driver.load_balancer, self.lb)
self.fail_completion.assert_called_once_with(self.context, self.lb)
self.assertEqual(0, self.succ_completion.call_count)
def test_thread_op_updates_vip_when_vip_delegated(self):
cfg.CONF.set_override('allocates_vip', True, group='octavia')
expected_vip = '10.1.1.1'
self.driver.req.get.side_effect = [
{'provisioning_status': 'PENDING_CREATE',
'vip': {'ip_address': ''}},
{'provisioning_status': 'ACTIVE',
'vip': {'ip_address': expected_vip}}
]
driver.thread_op(self.driver.load_balancer,
self.lb,
lb_create=True)
self.succ_completion.assert_called_once_with(self.context, self.lb,
delete=False,
lb_create=True)
self.assertEqual(expected_vip, self.lb.vip_address)