A10 Networks LBaaS v1 Driver
Change-Id: I32438c33ea28031c0d600e2a9266580e52dfbf54 Implements: blueprint a10networks-lbaas-driver
This commit is contained in:
parent
7c8c169578
commit
f2b4da6fc2
@ -611,5 +611,7 @@ service_provider=VPN:openswan:neutron.services.vpn.service_drivers.ipsec.IPsecVP
|
|||||||
# service_provider=VPN:cisco:neutron.services.vpn.service_drivers.cisco_ipsec.CiscoCsrIPsecVPNDriver:default
|
# service_provider=VPN:cisco:neutron.services.vpn.service_drivers.cisco_ipsec.CiscoCsrIPsecVPNDriver:default
|
||||||
# Uncomment the line below to use Embrane heleos as Load Balancer service provider.
|
# Uncomment the line below to use Embrane heleos as Load Balancer service provider.
|
||||||
# service_provider=LOADBALANCER:Embrane:neutron.services.loadbalancer.drivers.embrane.driver.EmbraneLbaas:default
|
# service_provider=LOADBALANCER:Embrane:neutron.services.loadbalancer.drivers.embrane.driver.EmbraneLbaas:default
|
||||||
|
# Uncomment the line below to use the A10 Networks LBaaS driver. Requires 'pip install a10-neutron-lbaas'.
|
||||||
|
#service_provider = LOADBALANCER:A10Networks:neutron.services.loadbalancer.drivers.a10networks.driver_v1.ThunderDriver:default
|
||||||
# Uncomment the following line to test the LBaaS v2 API _WITHOUT_ a real backend
|
# Uncomment the following line to test the LBaaS v2 API _WITHOUT_ a real backend
|
||||||
# service_provider = LOADBALANCER:LoggingNoop:neutron.services.loadbalancer.drivers.logging_noop.driver.LoggingNoopLoadBalancerDriver:default
|
# service_provider = LOADBALANCER:LoggingNoop:neutron.services.loadbalancer.drivers.logging_noop.driver.LoggingNoopLoadBalancerDriver:default
|
||||||
|
48
neutron/services/loadbalancer/drivers/a10networks/README.txt
Normal file
48
neutron/services/loadbalancer/drivers/a10networks/README.txt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
A10 Networks LBaaS Driver
|
||||||
|
|
||||||
|
Installation info:
|
||||||
|
|
||||||
|
To use this driver, you must:
|
||||||
|
- Install the a10-neutron-lbaas module. (E.g.: 'pip install a10-neutron-lbaas')
|
||||||
|
- Create a driver config file, a sample of which is given below.
|
||||||
|
- Enable it in neutron.conf
|
||||||
|
- Restart neutron-server
|
||||||
|
|
||||||
|
Third-party CI info:
|
||||||
|
|
||||||
|
Contact info for any problems is: a10-openstack-ci at a10networks dot com
|
||||||
|
Or contact Doug Wiegley directly (IRC: dougwig)
|
||||||
|
|
||||||
|
Configuration file:
|
||||||
|
|
||||||
|
Create a configuration file with a list of A10 appliances, similar to the
|
||||||
|
file below, located at:
|
||||||
|
/etc/neutron/services/loadbalancer/a10networks/config.py
|
||||||
|
|
||||||
|
Or you can override that directory by setting the environment
|
||||||
|
variable A10_CONFIG_DIR.
|
||||||
|
|
||||||
|
Example config file:
|
||||||
|
|
||||||
|
devices = {
|
||||||
|
"ax1": {
|
||||||
|
"name": "ax1",
|
||||||
|
"host": "10.10.100.20",
|
||||||
|
"port": 443,
|
||||||
|
"protocol": "https",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "a10",
|
||||||
|
"status": True,
|
||||||
|
"autosnat": False,
|
||||||
|
"api_version": "2.1",
|
||||||
|
"v_method": "LSI",
|
||||||
|
"max_instance": 5000,
|
||||||
|
"use_float": False,
|
||||||
|
"method": "hash"
|
||||||
|
},
|
||||||
|
"ax4": {
|
||||||
|
"host": "10.10.100.23",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "a10",
|
||||||
|
},
|
||||||
|
}
|
176
neutron/services/loadbalancer/drivers/a10networks/driver_v1.py
Normal file
176
neutron/services/loadbalancer/drivers/a10networks/driver_v1.py
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# Copyright 2014, Doug Wiegley (dougwig), A10 Networks
|
||||||
|
#
|
||||||
|
# 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 a10_neutron_lbaas
|
||||||
|
|
||||||
|
from neutron.db import l3_db
|
||||||
|
from neutron.db.loadbalancer import loadbalancer_db as lb_db
|
||||||
|
from neutron.openstack.common import log as logging
|
||||||
|
from neutron.plugins.common import constants
|
||||||
|
from neutron.services.loadbalancer.drivers import abstract_driver
|
||||||
|
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Most driver calls below are straight passthroughs to the A10 package
|
||||||
|
# 'a10_neutron_lbaas'. Any function that has not been fully abstracted
|
||||||
|
# into the openstack driver/plugin interface is NOT passed through, to
|
||||||
|
# make it obvious which hidden interfaces/db calls that we rely on.
|
||||||
|
|
||||||
|
class ThunderDriver(abstract_driver.LoadBalancerAbstractDriver):
|
||||||
|
|
||||||
|
def __init__(self, plugin):
|
||||||
|
LOG.debug("A10Driver: init version=%s", VERSION)
|
||||||
|
self.plugin = plugin
|
||||||
|
|
||||||
|
# Map the string types to neutron classes/functions, in order to keep
|
||||||
|
# from reaching into the bowels of Neutron from anywhere but this file.
|
||||||
|
self.neutron_map = {
|
||||||
|
'member': {
|
||||||
|
'model': lb_db.Member,
|
||||||
|
'delete_func': self.plugin._delete_db_member,
|
||||||
|
},
|
||||||
|
'pool': {
|
||||||
|
'model': lb_db.Pool,
|
||||||
|
'delete_func': self.plugin._delete_db_pool,
|
||||||
|
},
|
||||||
|
'vip': {
|
||||||
|
'model': lb_db.Vip,
|
||||||
|
'delete_func': self.plugin._delete_db_vip,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("A10Driver: initializing, version=%s, lbaas_manager=%s",
|
||||||
|
VERSION, a10_neutron_lbaas.VERSION)
|
||||||
|
|
||||||
|
self.a10 = a10_neutron_lbaas.A10OpenstackLBV1(self)
|
||||||
|
|
||||||
|
# The following private helper methods are used by a10_neutron_lbaas,
|
||||||
|
# and reflect the neutron interfaces required by that package.
|
||||||
|
|
||||||
|
def _hm_binding_count(self, context, hm_id):
|
||||||
|
return context.session.query(lb_db.PoolMonitorAssociation).filter_by(
|
||||||
|
monitor_id=hm_id).join(lb_db.Pool).count()
|
||||||
|
|
||||||
|
def _member_count(self, context, member):
|
||||||
|
return context.session.query(lb_db.Member).filter_by(
|
||||||
|
tenant_id=member['tenant_id'],
|
||||||
|
address=member['address']).count()
|
||||||
|
|
||||||
|
def _member_get(self, context, member_id):
|
||||||
|
return self.plugin.get_member(context, member_id)
|
||||||
|
|
||||||
|
def _member_get_ip(self, context, member, use_float=False):
|
||||||
|
ip_address = member['address']
|
||||||
|
if use_float:
|
||||||
|
fip_qry = context.session.query(l3_db.FloatingIP)
|
||||||
|
if (fip_qry.filter_by(fixed_ip_address=ip_address).count() > 0):
|
||||||
|
float_address = fip_qry.filter_by(
|
||||||
|
fixed_ip_address=ip_address).first()
|
||||||
|
ip_address = str(float_address.floating_ip_address)
|
||||||
|
return ip_address
|
||||||
|
|
||||||
|
def _pool_get_hm(self, context, hm_id):
|
||||||
|
return self.plugin.get_health_monitor(context, hm_id)
|
||||||
|
|
||||||
|
def _pool_get_tenant_id(self, context, pool_id):
|
||||||
|
pool_qry = context.session.query(lb_db.Pool).filter_by(id=pool_id)
|
||||||
|
z = pool_qry.first()
|
||||||
|
if z:
|
||||||
|
return z.tenant_id
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def _pool_get_vip_id(self, context, pool_id):
|
||||||
|
pool_qry = context.session.query(lb_db.Pool).filter_by(id=pool_id)
|
||||||
|
z = pool_qry.first()
|
||||||
|
if z:
|
||||||
|
return z.vip_id
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def _pool_total(self, context, tenant_id):
|
||||||
|
return context.session.query(lb_db.Pool).filter_by(
|
||||||
|
tenant_id=tenant_id).count()
|
||||||
|
|
||||||
|
def _vip_get(self, context, vip_id):
|
||||||
|
return self.plugin.get_vip(context, vip_id)
|
||||||
|
|
||||||
|
def _active(self, context, model_type, model_id):
|
||||||
|
self.plugin.update_status(context,
|
||||||
|
self.neutron_map[model_type]['model'],
|
||||||
|
model_id,
|
||||||
|
constants.ACTIVE)
|
||||||
|
|
||||||
|
def _failed(self, context, model_type, model_id):
|
||||||
|
self.plugin.update_status(context,
|
||||||
|
self.neutron_map[model_type]['model'],
|
||||||
|
model_id,
|
||||||
|
constants.ERROR)
|
||||||
|
|
||||||
|
def _db_delete(self, context, model_type, model_id):
|
||||||
|
self.neutron_map[model_type]['delete_func'](context, model_id)
|
||||||
|
|
||||||
|
def _hm_active(self, context, hm_id, pool_id):
|
||||||
|
self.plugin.update_pool_health_monitor(context, hm_id, pool_id,
|
||||||
|
constants.ACTIVE)
|
||||||
|
|
||||||
|
def _hm_failed(self, context, hm_id, pool_id):
|
||||||
|
self.plugin.update_pool_health_monitor(context, hm_id, pool_id,
|
||||||
|
constants.ERROR)
|
||||||
|
|
||||||
|
def _hm_db_delete(self, context, hm_id, pool_id):
|
||||||
|
self.plugin._delete_db_pool_health_monitor(context, hm_id, pool_id)
|
||||||
|
|
||||||
|
# Pass-through driver
|
||||||
|
|
||||||
|
def create_vip(self, context, vip):
|
||||||
|
self.a10.vip.create(context, vip)
|
||||||
|
|
||||||
|
def update_vip(self, context, old_vip, vip):
|
||||||
|
self.a10.vip.update(context, old_vip, vip)
|
||||||
|
|
||||||
|
def delete_vip(self, context, vip):
|
||||||
|
self.a10.vip.delete(context, vip)
|
||||||
|
|
||||||
|
def create_pool(self, context, pool):
|
||||||
|
self.a10.pool.create(context, pool)
|
||||||
|
|
||||||
|
def update_pool(self, context, old_pool, pool):
|
||||||
|
self.a10.pool.update(context, old_pool, pool)
|
||||||
|
|
||||||
|
def delete_pool(self, context, pool):
|
||||||
|
self.a10.pool.delete(context, pool)
|
||||||
|
|
||||||
|
def stats(self, context, pool_id):
|
||||||
|
return self.a10.pool.stats(context, pool_id)
|
||||||
|
|
||||||
|
def create_member(self, context, member):
|
||||||
|
self.a10.member.create(context, member)
|
||||||
|
|
||||||
|
def update_member(self, context, old_member, member):
|
||||||
|
self.a10.member.update(context, old_member, member)
|
||||||
|
|
||||||
|
def delete_member(self, context, member):
|
||||||
|
self.a10.member.delete(context, member)
|
||||||
|
|
||||||
|
def update_pool_health_monitor(self, context, old_hm, hm, pool_id):
|
||||||
|
self.a10.hm.update(context, old_hm, hm, pool_id)
|
||||||
|
|
||||||
|
def create_pool_health_monitor(self, context, hm, pool_id):
|
||||||
|
self.a10.hm.create(context, hm, pool_id)
|
||||||
|
|
||||||
|
def delete_pool_health_monitor(self, context, hm, pool_id):
|
||||||
|
self.a10.hm.delete(context, hm, pool_id)
|
@ -0,0 +1,179 @@
|
|||||||
|
# Copyright 2014, Doug Wiegley (dougwig), A10 Networks
|
||||||
|
#
|
||||||
|
# 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 sys
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from neutron import context
|
||||||
|
from neutron.db.loadbalancer import loadbalancer_db as lb_db
|
||||||
|
with mock.patch.dict(sys.modules, {'a10_neutron_lbaas': mock.Mock()}):
|
||||||
|
from neutron.services.loadbalancer.drivers.a10networks import driver_v1
|
||||||
|
from neutron.tests.unit.db.loadbalancer import test_db_loadbalancer
|
||||||
|
|
||||||
|
|
||||||
|
def fake_model(id):
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'tenant_id': "tennant-was-a-great-doctor"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fake_member(id):
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'tenant_id': "vippyvip",
|
||||||
|
'address': '1.1.1.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestA10ThunderDriver(test_db_loadbalancer.LoadBalancerPluginDbTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestA10ThunderDriver, self).setUp()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
self.plugin = mock.Mock()
|
||||||
|
self.driver = driver_v1.ThunderDriver(self.plugin)
|
||||||
|
self.driver.a10 = mock.Mock()
|
||||||
|
self.m = fake_model('p1')
|
||||||
|
|
||||||
|
def test__hm_binding_count(self):
|
||||||
|
n = self.driver._hm_binding_count(self.context, 'hm01')
|
||||||
|
self.assertEqual(n, 0)
|
||||||
|
|
||||||
|
def test__member_count(self):
|
||||||
|
self.m = fake_member('mem1')
|
||||||
|
n = self.driver._member_count(self.context, self.m)
|
||||||
|
self.assertEqual(n, 0)
|
||||||
|
|
||||||
|
def test__member_get_ip(self):
|
||||||
|
self.m = fake_member('mem1')
|
||||||
|
z = self.driver._member_get_ip(self.context, self.m, False)
|
||||||
|
self.assertEqual(z, '1.1.1.1')
|
||||||
|
z = self.driver._member_get_ip(self.context, self.m, True)
|
||||||
|
self.assertEqual(z, '1.1.1.1')
|
||||||
|
|
||||||
|
def test__pool_get_hm(self):
|
||||||
|
self.driver._pool_get_hm(self.context, 'hm01')
|
||||||
|
self.plugin.get_health_monitor.assert_called_once_with(
|
||||||
|
self.context, 'hm01')
|
||||||
|
|
||||||
|
def test__pool_get_tenant_id(self):
|
||||||
|
z = self.driver._pool_get_tenant_id(self.context, 'pool1')
|
||||||
|
self.assertEqual(z, '')
|
||||||
|
|
||||||
|
def test__pool_get_vip_id(self):
|
||||||
|
z = self.driver._pool_get_vip_id(self.context, 'pool1')
|
||||||
|
self.assertEqual(z, '')
|
||||||
|
|
||||||
|
def test__pool_total(self):
|
||||||
|
n = self.driver._pool_total(self.context,
|
||||||
|
tenant_id='whatareyoudoingdave')
|
||||||
|
self.assertEqual(n, 0)
|
||||||
|
|
||||||
|
def test__active(self):
|
||||||
|
self.driver._active(self.context, 'vip', 'vip1')
|
||||||
|
self.plugin.update_status.assert_called_once_with(
|
||||||
|
self.context, lb_db.Vip, 'vip1', 'ACTIVE')
|
||||||
|
|
||||||
|
def test__failed(self):
|
||||||
|
self.driver._failed(self.context, 'vip', 'vip2-1-2')
|
||||||
|
self.plugin.update_status.assert_called_once_with(
|
||||||
|
self.context, lb_db.Vip, 'vip2-1-2', 'ERROR')
|
||||||
|
|
||||||
|
def test__db_delete(self):
|
||||||
|
self.driver._db_delete(self.context, 'pool', 'myid0101')
|
||||||
|
self.plugin._delete_db_pool.assert_called_once_with(
|
||||||
|
self.context, 'myid0101')
|
||||||
|
|
||||||
|
def test__hm_active(self):
|
||||||
|
self.driver._hm_active(self.context, 'hm01', 'pool1')
|
||||||
|
self.plugin.update_pool_health_monitor.assert_called_once_with(
|
||||||
|
self.context, 'hm01', 'pool1', 'ACTIVE')
|
||||||
|
|
||||||
|
def test__hm_failed(self):
|
||||||
|
self.driver._hm_failed(self.context, 'hm01', 'pool1')
|
||||||
|
self.plugin.update_pool_health_monitor.assert_called_once_with(
|
||||||
|
self.context, 'hm01', 'pool1', 'ERROR')
|
||||||
|
|
||||||
|
def test__hm_db_delete(self):
|
||||||
|
self.driver._hm_db_delete(self.context, 'hm01', 'pool2')
|
||||||
|
self.plugin._delete_db_pool_health_monitor.assert_called_once_with(
|
||||||
|
self.context, 'hm01', 'pool2')
|
||||||
|
|
||||||
|
def test_create_vip(self):
|
||||||
|
self.driver.create_vip(self.context, self.m)
|
||||||
|
self.driver.a10.vip.create.assert_called_once_with(
|
||||||
|
self.context, self.m)
|
||||||
|
|
||||||
|
def test_update_vip(self):
|
||||||
|
self.driver.update_vip(self.context, self.m, self.m)
|
||||||
|
self.driver.a10.vip.update.assert_called_once_with(
|
||||||
|
self.context, self.m, self.m)
|
||||||
|
|
||||||
|
def test_delete_vip(self):
|
||||||
|
self.driver.delete_vip(self.context, self.m)
|
||||||
|
self.driver.a10.vip.delete.assert_called_once_with(
|
||||||
|
self.context, self.m)
|
||||||
|
|
||||||
|
def test_create_pool(self):
|
||||||
|
self.driver.create_pool(self.context, self.m)
|
||||||
|
self.driver.a10.pool.create.assert_called_once_with(
|
||||||
|
self.context, self.m)
|
||||||
|
|
||||||
|
def test_update_pool(self):
|
||||||
|
self.driver.update_pool(self.context, self.m, self.m)
|
||||||
|
self.driver.a10.pool.update.assert_called_once_with(
|
||||||
|
self.context, self.m, self.m)
|
||||||
|
|
||||||
|
def test_delete_pool(self):
|
||||||
|
self.driver.delete_pool(self.context, self.m)
|
||||||
|
self.driver.a10.pool.delete.assert_called_once_with(
|
||||||
|
self.context, self.m)
|
||||||
|
|
||||||
|
def test_stats(self):
|
||||||
|
self.driver.stats(self.context, self.m['id'])
|
||||||
|
self.driver.a10.pool.stats.assert_called_once_with(
|
||||||
|
self.context, self.m['id'])
|
||||||
|
|
||||||
|
def test_create_member(self):
|
||||||
|
self.driver.create_member(self.context, self.m)
|
||||||
|
self.driver.a10.member.create.assert_called_once_with(
|
||||||
|
self.context, self.m)
|
||||||
|
|
||||||
|
def test_update_member(self):
|
||||||
|
self.driver.update_member(self.context, self.m, self.m)
|
||||||
|
self.driver.a10.member.update.assert_called_once_with(
|
||||||
|
self.context, self.m, self.m)
|
||||||
|
|
||||||
|
def test_delete_member(self):
|
||||||
|
self.driver.delete_member(self.context, self.m)
|
||||||
|
self.driver.a10.member.delete.assert_called_once_with(
|
||||||
|
self.context, self.m)
|
||||||
|
|
||||||
|
def test_update_pool_health_monitor(self):
|
||||||
|
self.driver.update_pool_health_monitor(self.context, self.m, self.m,
|
||||||
|
'pool1')
|
||||||
|
self.driver.a10.hm.update.assert_called_once_with(
|
||||||
|
self.context, self.m, self.m, 'pool1')
|
||||||
|
|
||||||
|
def test_create_pool_health_monitor(self):
|
||||||
|
self.driver.create_pool_health_monitor(self.context, self.m, 'pool1')
|
||||||
|
self.driver.a10.hm.create.assert_called_once_with(
|
||||||
|
self.context, self.m, 'pool1')
|
||||||
|
|
||||||
|
def test_delete_pool_health_monitor(self):
|
||||||
|
self.driver.delete_pool_health_monitor(self.context, self.m, 'pool1')
|
||||||
|
self.driver.a10.hm.delete.assert_called_once_with(
|
||||||
|
self.context, self.m, 'pool1')
|
Loading…
x
Reference in New Issue
Block a user