nb: add support for lb health checks API

This change adds functions and commands to add, delete, list, get and
update health checks of load balancers. Also added a method to get one
load balancer by name or uuid and methods to modify 'ip_port_mappings'
column in the load balancer.

Closes-Bug: 1964382
Change-Id: I3f8b63c64c7ac9c570dbd400c562fa97840429ca
This commit is contained in:
Anton Vazhnetsov 2022-03-05 20:40:10 +03:00 committed by Terry Wilson
parent e9c6c6cc13
commit f3c5da5402
5 changed files with 307 additions and 0 deletions

View File

@ -885,6 +885,88 @@ class API(api.API, metaclass=abc.ABCMeta):
def lb_list(self):
"""Get the UUIDs of all load balanacers"""
@abc.abstractmethod
def lb_get(self, lb):
"""Get load balancer for 'lb'
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def lb_add_health_check(self, lb, vip, **options):
"""Add health check for 'lb'
:param lb: The name or uuid of a load balancer
:type lb: string or uuid.UUID
:param vip: A virtual IP
:type vip: string
:param options: keys and values for the port 'options' dict
:type options: key: string, value: string
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def lb_del_health_check(self, lb, hc_uuid, if_exists=False):
"""Remove health check from 'lb'
:param lb: The name or uuid of a load balancer
:type lb: string or uuid.UUID
:param hc_uuid: uuid of a health check
:type hc_uuid: uuid.UUID
:param if_exists: If True, don't fail if the hc_uuid doesn't exist
:type if_exists: boolean
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def lb_add_ip_port_mapping(self, lb, endpoint_ip, port_name, source_ip):
"""Add IP port mapping to 'lb'
Maps from endpoint IP to a colon-separated pair of logical port name
and source IP, e.g. port_name:sourc_ip.
:param lb: The name or uuid of a load balancer
:type lb: string or uuid.UUID
:param endpoint_ip: IPv4 address
:type endpoint_ip: string
:param port_name: The name of a logical port
:type port_name: string
:param source_ip: IPv4 address
:type source_ip: string
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def lb_del_ip_port_mapping(self, lb, endpoint_ip):
"""Remove IP port mapping from 'lb'
:param lb: The name or uuid of a load balancer
:type lb: string or uuid.UUID
:param endpoint_ip: IPv4 address
:type endpoint_ip: string
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def health_check_set_options(self, hc_uuid, **options):
"""Set options to the 'health_check'
:param hc_uuid: uuid of the health check
:type hc_uuid: uuid.UUID
:param options: keys and values for the port 'options' dict
:type options: key: string, value: string
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def health_check_get_options(self, hc_uuid):
"""Get the options for 'health_check'
:param hc_uuid: uuid of the health check
:type hc_uuid: uuid.UUID
:returns: :class:`Command` with dict result
"""
@abc.abstractmethod
def lr_lb_add(self, router, lb, may_exist=False):
"""Add a load-balancer to 'router'

View File

@ -1341,6 +1341,100 @@ class LbListCommand(cmd.ReadOnlyCommand):
for r in self.api.tables['Load_Balancer'].rows.values()]
class LbGetCommand(cmd.BaseGetRowCommand):
table = 'Load_Balancer'
class LbAddHealthCheckCommand(cmd.BaseCommand):
table = 'Load_Balancer'
def __init__(self, api, lb, vip, **options):
super().__init__(api)
self.lb = lb
self.vip = vip
self.options = options
def run_idl(self, txn):
lb = self.api.lookup(self.table, self.lb)
cmd = HealthCheckAddCommand(self.api, self.vip, **self.options)
cmd.run_idl(txn)
lb.addvalue('health_check', cmd.result)
class LbDelHealthCheckCommand(cmd.BaseCommand):
table = 'Load_Balancer'
def __init__(self, api, lb, hc_uuid, if_exists=False):
super().__init__(api)
self.lb = lb
self.hc_uuid = hc_uuid
self.if_exists = if_exists
def run_idl(self, txn):
lb = self.api.lookup(self.table, self.lb)
for health_check in lb.health_check:
if health_check.uuid == self.hc_uuid:
lb.delvalue('health_check', health_check)
health_check.delete()
return
if not self.if_exists:
msg = "Health check '%s' on lb %s does not exist" % (
self.hc_uuid, self.lb)
raise RuntimeError(msg)
class LbAddIpPortMappingСommand(cmd.BaseCommand):
table = 'Load_Balancer'
def __init__(self, api, lb, endpoint_ip, port_name, source_ip):
super().__init__(api)
self.lb = lb
self.endpoint_ip = str(netaddr.IPAddress(endpoint_ip))
self.port_name = port_name
self.source_ip = str(netaddr.IPAddress(source_ip))
def run_idl(self, txn):
lb = self.api.lookup(self.table, self.lb)
lb.setkey('ip_port_mappings', self.endpoint_ip,
'%s:%s' % (self.port_name, self.source_ip))
class LbDelIpPortMappingCommand(cmd.BaseCommand):
table = 'Load_Balancer'
def __init__(self, api, lb, endpoint_ip):
super().__init__(api)
self.lb = lb
self.endpoint_ip = str(netaddr.IPAddress(endpoint_ip))
def run_idl(self, txn):
lb = self.api.lookup(self.table, self.lb)
lb.delkey('ip_port_mappings', self.endpoint_ip)
class HealthCheckAddCommand(cmd.AddCommand):
table_name = 'Load_Balancer_Health_Check'
def __init__(self, api, vip, **options):
super().__init__(api)
self.vip = utils.normalize_ip_port(vip)
self.options = options
def run_idl(self, txn):
hc = txn.insert(self.api.tables[self.table_name])
hc.vip = self.vip
hc.options = self.options
self.result = hc
class HealthCheckSetOptionsCommand(cmd.BaseSetOptionsCommand):
table = 'Load_Balancer_Health_Check'
class HealthCheckGetOptionsCommand(cmd.BaseGetOptionsCommand):
table = 'Load_Balancer_Health_Check'
class LrLbAddCommand(cmd.BaseCommand):
def __init__(self, api, router, lb, may_exist=False):
super().__init__(api)

View File

@ -273,6 +273,28 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
def lb_list(self):
return cmd.LbListCommand(self)
def lb_get(self, lb):
return cmd.LbGetCommand(self, lb)
def lb_add_health_check(self, lb, vip, **options):
return cmd.LbAddHealthCheckCommand(self, lb, vip, **options)
def lb_del_health_check(self, lb, hc_uuid, if_exists=False):
return cmd.LbDelHealthCheckCommand(self, lb, hc_uuid, if_exists)
def lb_add_ip_port_mapping(self, lb, endport_ip, port_name, source_ip):
return cmd.LbAddIpPortMappingСommand(self, lb, endport_ip,
port_name, source_ip)
def lb_del_ip_port_mapping(self, lb, endport_ip):
return cmd.LbDelIpPortMappingCommand(self, lb, endport_ip)
def health_check_set_options(self, hc_uuid, **options):
return cmd.HealthCheckSetOptionsCommand(self, hc_uuid, **options)
def health_check_get_options(self, hc_uuid):
return cmd.HealthCheckGetOptionsCommand(self, hc_uuid)
def lr_lb_add(self, router, lb, may_exist=False):
return cmd.LrLbAddCommand(self, router, lb, may_exist)

View File

@ -1669,6 +1669,109 @@ class TestLoadBalancerOps(OvnNorthboundTest):
lbset = self.api.lb_list().execute(check_error=True)
self.assertTrue(lbs.issubset(lbset))
def _test_lb_get(self, col):
lb = self._lb_add(utils.get_rand_device_name(),
'192.0.0.1', ['10.0.0.1'])
val = getattr(lb, col)
found = self.api.lb_get(val).execute(check_error=True)
self.assertEqual(lb, found)
def test_lb_get_uuid(self):
self._test_lb_get('uuid')
def test_lb_get_name(self):
self._test_lb_get('name')
def _test_lb_add_del_health_check(self, col):
hc_options = {
'interval': '2',
'timeout': '10',
'success_count': '3',
'failure_count': '3',
}
hc_vip = '172.31.0.1'
lb = self._lb_add(utils.get_rand_device_name(),
'192.0.0.1', ['10.0.0.1'])
self.assertEqual(lb.health_check, [])
val = getattr(lb, col)
self.api.lb_add_health_check(val,
hc_vip,
**hc_options).execute(check_error=True)
self.assertEqual(len(lb.health_check), 1)
hc = self.api.lookup('Load_Balancer_Health_Check',
lb.health_check[0].uuid)
self.assertEqual(hc.vip, hc_vip)
self.assertEqual(hc.options, hc_options)
self.api.lb_del_health_check(val, hc.uuid).execute(check_error=True)
self.assertEqual(len(lb.health_check), 0)
self.assertNotIn(hc.uuid,
self.api.tables['Load_Balancer_Health_Check'].rows)
def test_lb_add_del_health_check_uuid(self):
self._test_lb_add_del_health_check('uuid')
def test_lb_add_del_health_check_name(self):
self._test_lb_add_del_health_check('name')
def test_lb_del_health_check_if_exists(self):
lb = self._lb_add(utils.get_rand_device_name(),
'192.0.0.1', ['10.0.0.1'])
self.api.lb_del_health_check(lb.name, uuid.uuid4(),
if_exists=True).execute(check_error=True)
def _test_lb_add_del_ip_port_mapping(self, col):
endpoint_ip = '172.31.0.4'
port_name = 'sw1-p1'
source_ip = '172.31.0.6'
lb = self._lb_add(utils.get_rand_device_name(),
'192.0.0.1', ['10.0.0.1'])
self.assertEqual(lb.ip_port_mappings, {})
val = getattr(lb, col)
self.api.lb_add_ip_port_mapping(val,
endpoint_ip,
port_name,
source_ip).execute(check_error=True)
self.assertEqual(lb.ip_port_mappings[endpoint_ip],
'%s:%s' % (port_name, source_ip))
self.api.lb_del_ip_port_mapping(val,
endpoint_ip).execute(check_error=True)
self.assertEqual(lb.ip_port_mappings, {})
def test_lb_add_del_ip_port_mapping_uuid(self):
self._test_lb_add_del_ip_port_mapping('uuid')
def test_lb_add_del_ip_port_mapping_name(self):
self._test_lb_add_del_ip_port_mapping('name')
def test_hc_get_set_options(self):
hc_options = {
'interval': '2',
'timeout': '10',
'success_count': '3',
'failure_count': '3',
}
lb = self._lb_add(utils.get_rand_device_name(),
'192.0.0.1', ['10.0.0.1'])
self.api.lb_add_health_check(lb.uuid,
'172.31.0.1',
**hc_options).execute(check_error=True)
hc = self.api.lookup('Load_Balancer_Health_Check',
lb.health_check[0].uuid)
options = self.api.health_check_get_options(
hc.uuid).execute(check_error=True)
self.assertEqual(hc_options, options)
options.update({
'interval': '5',
'new-option': 'option',
})
self.api.health_check_set_options(hc.uuid,
**options).execute(check_error=True)
self.assertEqual(hc.options, options)
class TestObLbOps(testscenarios.TestWithScenarios, OvnNorthboundTest):
scenarios = [

View File

@ -0,0 +1,6 @@
---
features:
- |
Added functions and commands to add, delete, list and update records of
'Load_Balancer_Health_Check' table. Also added a method to get one load balancer
by name or uuid and methods to modify 'ip_port_mappings' column in the load balancer.