nb: provide 'route_table' in lr-route-{add,del,list}

Adds possibility to specify 'route_table' in methods for static
routes.

See this commit for more information:
1655a6c146

Closes-bug: 2037652

Change-Id: Ic965ae098adb6db2e5a057eee74250e3b6331b01
This commit is contained in:
Anton Vazhnetsov 2021-07-28 19:29:51 +03:00 committed by Anton Vazhnetsov
parent 033b6e3bd2
commit dae455b6ee
5 changed files with 114 additions and 21 deletions

View File

@ -39,5 +39,6 @@ PROTO_TCP = 'tcp'
PROTO_UDP = 'udp'
ROUTE_DISCARD = "discard"
MAIN_ROUTE_TABLE = ""
LOCALNET = 'localnet'

View File

@ -735,7 +735,8 @@ class API(api.API, metaclass=abc.ABCMeta):
@abc.abstractmethod
def lr_route_add(self, router, prefix, nexthop, port=None,
policy='dst-ip', may_exist=False, ecmp=False):
policy='dst-ip', may_exist=False, ecmp=False,
route_table=const.MAIN_ROUTE_TABLE):
"""Add a route to 'router'
:param router: The name or uuid of the router
@ -758,11 +759,14 @@ class API(api.API, metaclass=abc.ABCMeta):
same IP prefix is allowed as long as the nexthop is
different
:type ecmp: boolean
:param route_table: The name of route table
:type route_table: str
returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def lr_route_del(self, router, prefix=None, if_exists=False, nexthop=None):
def lr_route_del(self, router, prefix=None, if_exists=False, nexthop=None,
route_table=const.MAIN_ROUTE_TABLE):
"""Remove routes from 'router'
:param router: The name or uuid of the router
@ -775,16 +779,21 @@ class API(api.API, metaclass=abc.ABCMeta):
the IP address of one of `router`'s logical router
ports or the IP address of a logical port
:type nexthop: string
:param route_table: The name of route table
:type route_table: str
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def lr_route_list(self, router):
def lr_route_list(self, router, route_table=None):
"""Get the UUIDs of static logical routes from 'router'
:param router: The name or uuid of the router
:type router: string or uuid.UUID
:returns: :class:`Command` with RowView list result
:param router: The name or uuid of the router
:type router: string or uuid.UUID
:param route_table: The name of route table. Pass "" to get routes of
global route table only
:type route_table: str
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod

View File

@ -1139,7 +1139,8 @@ class BFDGetCommand(cmd.BaseGetRowCommand):
class LrRouteAddCommand(cmd.BaseCommand):
def __init__(self, api, router, prefix, nexthop, port=None,
policy='dst-ip', may_exist=False, ecmp=False):
policy='dst-ip', may_exist=False, ecmp=False,
route_table=const.MAIN_ROUTE_TABLE):
prefix = str(netaddr.IPNetwork(prefix))
if nexthop != const.ROUTE_DISCARD:
nexthop = str(netaddr.IPAddress(nexthop))
@ -1150,12 +1151,16 @@ class LrRouteAddCommand(cmd.BaseCommand):
self.port = port
self.policy = policy
self.ecmp = ecmp
self.route_table = route_table
self.may_exist = may_exist
def run_idl(self, txn):
lr = self.api.lookup('Logical_Router', self.router)
for route in lr.static_routes:
if self.prefix == route.ip_prefix:
if (
self.prefix == route.ip_prefix and
self.route_table == route.route_table
):
if self.ecmp and self.nexthop != route.nexthop:
continue
if not self.may_exist:
@ -1172,6 +1177,7 @@ class LrRouteAddCommand(cmd.BaseCommand):
route.ip_prefix = self.prefix
route.nexthop = self.nexthop
route.policy = self.policy
route.route_table = self.route_table
if self.port:
route.output_port = self.port
lr.addvalue('static_routes', route)
@ -1187,13 +1193,14 @@ class LrRouteAddCommand(cmd.BaseCommand):
class LrRouteDelCommand(cmd.BaseCommand):
def __init__(self, api, router, prefix=None, if_exists=False,
nexthop=None):
nexthop=None, route_table=const.MAIN_ROUTE_TABLE):
if prefix is not None:
prefix = str(netaddr.IPNetwork(prefix))
super().__init__(api)
self.router = router
self.prefix = prefix
self.nexthop = nexthop
self.route_table = route_table
self.if_exists = if_exists
def run_idl(self, txn):
@ -1202,7 +1209,10 @@ class LrRouteDelCommand(cmd.BaseCommand):
lr.static_routes = []
return
for route in lr.static_routes:
if self.prefix == route.ip_prefix:
if (
self.prefix == route.ip_prefix and
self.route_table == route.route_table
):
if self.nexthop and route.nexthop != self.nexthop:
continue
@ -1216,13 +1226,19 @@ class LrRouteDelCommand(cmd.BaseCommand):
class LrRouteListCommand(cmd.ReadOnlyCommand):
def __init__(self, api, router):
def __init__(self, api, router, route_table=None):
super().__init__(api)
self.router = router
self.route_table = route_table
def run_idl(self, txn):
lr = self.api.lookup('Logical_Router', self.router)
self.result = [rowview.RowView(r) for r in lr.static_routes]
if self.route_table is not None:
self.result = [rowview.RowView(r)
for r in lr.static_routes
if r.route_table == self.route_table]
else:
self.result = [rowview.RowView(r) for r in lr.static_routes]
class LrNatAddCommand(cmd.BaseCommand):

View File

@ -236,15 +236,18 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
return cmd.LrpDelNetworksCommand(self, port, networks, if_exists)
def lr_route_add(self, router, prefix, nexthop, port=None,
policy='dst-ip', may_exist=False, ecmp=False):
policy='dst-ip', may_exist=False, ecmp=False,
route_table=const.MAIN_ROUTE_TABLE):
return cmd.LrRouteAddCommand(self, router, prefix, nexthop, port,
policy, may_exist, ecmp)
policy, may_exist, ecmp, route_table)
def lr_route_del(self, router, prefix=None, if_exists=False, nexthop=None):
return cmd.LrRouteDelCommand(self, router, prefix, if_exists, nexthop)
def lr_route_del(self, router, prefix=None, if_exists=False, nexthop=None,
route_table=const.MAIN_ROUTE_TABLE):
return cmd.LrRouteDelCommand(self, router, prefix, if_exists, nexthop,
route_table)
def lr_route_list(self, router):
return cmd.LrRouteListCommand(self, router)
def lr_route_list(self, router, route_table=None):
return cmd.LrRouteListCommand(self, router, route_table)
def lr_nat_add(self, router, nat_type, external_ip, logical_ip,
logical_port=None, external_mac=None, may_exist=False):

View File

@ -956,14 +956,18 @@ class TestLogicalRouterOps(OvnNorthboundTest):
self.assertTrue(lrs.issubset(lr_set), "%s vs %s" % (lrs, lr_set))
def _lr_add_route(self, router=None, prefix=None, nexthop=None, port=None,
ecmp=False, **kwargs):
ecmp=False, route_table=const.MAIN_ROUTE_TABLE,
**kwargs):
lr = self._lr_add(router or utils.get_rand_device_name(),
may_exist=True)
prefix = prefix or '192.0.2.0/25'
nexthop = nexthop or '192.0.2.254'
port = port or "port_name"
sr = self.api.lr_route_add(lr.uuid, prefix, nexthop, port, ecmp=ecmp,
**kwargs).execute(check_error=True)
sr = self.api.lr_route_add(
lr.uuid, prefix, nexthop, port,
ecmp=ecmp, route_table=route_table,
**kwargs
).execute(check_error=True)
self.assertIn(sr, lr.static_routes)
self.assertEqual(prefix, sr.ip_prefix)
self.assertEqual(nexthop, sr.nexthop)
@ -1003,6 +1007,20 @@ class TestLogicalRouterOps(OvnNorthboundTest):
self.assertRaises(netaddr.AddrFormatError, self._lr_add_route,
prefix='not-discard')
def test_lr_route_add_route_table(self):
lr = self._lr_add()
route_table = "route-table"
# add route to 'main' route table
route = self._lr_add_route(lr.name)
self.assertEqual(route.route_table, const.MAIN_ROUTE_TABLE)
route = self._lr_add_route(lr.name, route_table=route_table)
self.assertEqual(route.route_table, route_table)
self.assertEqual(
len(self.api.tables['Logical_Router_Static_Route'].rows), 2)
def test_lr_route_del(self):
prefix = "192.0.2.0/25"
route = self._lr_add_route(prefix=prefix)
@ -1057,6 +1075,29 @@ class TestLogicalRouterOps(OvnNorthboundTest):
self.assertEqual(
len(self.api.tables['Logical_Router_Static_Route'].rows), 1)
def test_lr_route_del_route_table(self):
lr = self._lr_add()
route_table = "route-table"
route_in_main = self._lr_add_route(lr.uuid, prefix="10.0.0.0/24")
route = self._lr_add_route(
lr.uuid, prefix="10.0.1.0/24", route_table=route_table)
self.assertEqual(len(lr.static_routes), 2)
# try to delete from the 'main' table implicitly
cmd = self.api.lr_route_del(lr.uuid, route.ip_prefix)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
self.api.lr_route_del(
lr.uuid, prefix=route.ip_prefix, route_table=route_table
).execute(check_error=True)
self.assertEqual(len(lr.static_routes), 1)
self.api.lr_route_del(
lr.uuid, route_in_main.ip_prefix).execute(check_error=True)
self.assertEqual(len(lr.static_routes), 0)
def test_lr_route_list(self):
lr = self._lr_add()
routes = {self._lr_add_route(lr.uuid, prefix="192.0.%s.0/25" % p)
@ -1065,6 +1106,29 @@ class TestLogicalRouterOps(OvnNorthboundTest):
check_error=True))
self.assertTrue(routes.issubset(route_set))
def test_lr_route_list_route_table(self):
lr = self._lr_add()
route_table = "route-table"
prefix1 = "10.0.0.0/24"
prefix2 = "10.0.1.0/24"
self._lr_add_route(lr.uuid, prefix=prefix1)
self._lr_add_route(lr.uuid, prefix=prefix2, route_table=route_table)
routes = self.api.lr_route_list(lr.uuid).execute(check_error=True)
self.assertEqual(len(routes), 2) # all routes in logical router
for route_table, prefix in zip(
[const.MAIN_ROUTE_TABLE, route_table],
[prefix1, prefix2]
):
routes = self.api.lr_route_list(
lr.uuid, route_table=route_table).execute(check_error=True)
self.assertEqual(len(routes), 1)
self.assertEqual(routes[0].ip_prefix, prefix)
self.assertEqual(routes[0].route_table, route_table)
def _lr_nat_add(self, *args, **kwargs):
lr = kwargs.pop('router', self._lr_add(utils.get_rand_device_name()))
nat = self.api.lr_nat_add(