nb: add support for set of addresses API

This change adds functions and commands to add, delete, list, get and
update sets of addresses.

Closes-Bug: #1951110
Change-Id: I9390bf5e3586a3dd28c3743d06578c2a8f262e77
This commit is contained in:
Anton Vazhnetsov 2021-11-13 23:45:21 +03:00
parent 0e4c7a056c
commit 641b6b857e
6 changed files with 281 additions and 0 deletions

View File

@ -206,6 +206,68 @@ class API(api.API, metaclass=abc.ABCMeta):
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
def address_set_add(self, name, addresses=None, may_exist=False):
"""Create a set of addresses named 'name'
:param name: The name of the address set
:type name: string
:param addresses: One or more IP addresses to add to the address set
:type addresses: list of strings
:param may_exist: If True, don't fail if the address set already exists
:type may_exist: boolean
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def address_set_del(self, address_set, if_exists=False):
"""Delete a set of addresses 'address_set'
:param address_set: The name of the address set
:type address_set: string or uuid.UUID
:param if_exists: Do not fail if the Address_set row does not exist
:type if_exists: bool
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def address_set_get(self, address_set):
"""Get a set of addresses for 'address_set'
:param address_set: The name of the address set
:type address_set: string or uuid.UUID
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def address_set_list(self):
"""Get all sets of addresses
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
def address_set_add_addresses(self, address_set, addresses):
"""Add a list of addresses to 'address_set'
:param address_set: The name of the address set
:type address_set: string or uuid.UUID
:param addresses: One or more IP addresses to add to the address set
:type addresses: string or list of strings
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def address_set_remove_addresses(self, address_set, addresses):
"""Remove a list of addresses from 'address_set'
:param address_set: The name of the address set
:type address_set: string or uuid.UUID
:param addresses: One or more IP addresses to remove from address set
:type addresses: string of list of strings
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def qos_add(self, switch, direction, priority, match, rate=None,
burst=None, dscp=None, may_exist=False, **columns):

View File

@ -214,6 +214,88 @@ class PgAclListCommand(_AclListHelper):
lookup_table = 'Port_Group'
class AddressSetAddCommand(cmd.AddCommand):
table_name = 'Address_Set'
def __init__(self, api, name, addresses=None, may_exist=False):
super().__init__(api)
self.name = name
self.addresses = [str(netaddr.IPAddress(address))
for address in addresses or []]
self.may_exist = may_exist
def run_idl(self, txn):
address_set = self.api.lookup(self.table_name, self.name, None)
if address_set:
if self.may_exist:
self.result = rowview.RowView(address_set)
return
raise RuntimeError("Address set %s exists" % self.name)
address_set = txn.insert(self.api.tables[self.table_name])
address_set.name = self.name
if self.addresses:
address_set.addresses = self.addresses
self.result = address_set.uuid
class AddressSetDelCommand(cmd.BaseCommand):
table_name = 'Address_Set'
def __init__(self, api, address_set, if_exists=False):
super().__init__(api)
self.address_set = address_set
self.if_exists = if_exists
def run_idl(self, txn):
try:
address_set = self.api.lookup(self.table_name, self.address_set)
address_set.delete()
except idlutils.RowNotFound as e:
if self.if_exists:
return
msg = "Address set %s does not exist" % self.address_set
raise RuntimeError(msg) from e
class AddressSetGetCommand(cmd.BaseGetRowCommand):
table = 'Address_Set'
class AddressSetListCommand(cmd.ReadOnlyCommand):
table = 'Address_Set'
def run_idl(self, txn):
self.result = [rowview.RowView(r) for
r in self.api.tables[self.table].rows.values()]
class AddressSetUpdateAddressesCommand(cmd.BaseCommand):
table_name = 'Address_Set'
def __init__(self, api, address_set, addresses):
super().__init__(api)
self.address_set = address_set
if isinstance(addresses, (str, bytes)):
addresses = [addresses]
self.addresses = [str(netaddr.IPAddress(address))
for address in addresses]
class AddressSetAddAddressesCommand(AddressSetUpdateAddressesCommand):
def run_idl(self, txn):
address_set = self.api.lookup(self.table_name, self.address_set)
for address in self.addresses:
address_set.addvalue('addresses', address)
class AddressSetRemoveAddressCommand(AddressSetUpdateAddressesCommand):
def run_idl(self, txn):
address_set = self.api.lookup(self.table_name, self.address_set)
for address in self.addresses:
address_set.delvalue('addresses', address)
class QoSAddCommand(cmd.AddCommand):
table_name = 'QoS'

View File

@ -80,6 +80,24 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
def pg_acl_list(self, port_group):
return cmd.PgAclListCommand(self, port_group)
def address_set_add(self, name, addresses=None, may_exist=False):
return cmd.AddressSetAddCommand(self, name, addresses, may_exist)
def address_set_del(self, address_set, if_exists=False):
return cmd.AddressSetDelCommand(self, address_set, if_exists)
def address_set_get(self, address_set):
return cmd.AddressSetGetCommand(self, address_set)
def address_set_list(self):
return cmd.AddressSetListCommand(self)
def address_set_add_addresses(self, address_set, addresses):
return cmd.AddressSetAddAddressesCommand(self, address_set, addresses)
def address_set_remove_addresses(self, address_set, addresses):
return cmd.AddressSetRemoveAddressCommand(self, address_set, addresses)
def qos_add(self, switch, direction, priority, match, rate=None,
burst=None, dscp=None, may_exist=False, **columns):
return cmd.QoSAddCommand(self, switch, direction, priority, match,

View File

@ -45,6 +45,11 @@ class PortGroupFixture(fixtures.ImplIdlFixture):
delete = 'pg_del'
class AddressSetFixture(fixtures.ImplIdlFixture):
create = 'address_set_add'
delete = 'address_set_del'
class MeterFixture(fixtures.ImplIdlFixture):
create = 'meter_add'
delete = 'meter_del'

View File

@ -236,6 +236,116 @@ class TestAclOps(OvnNorthboundTest):
self.assertIn(r2, acls)
class TestAddressSetOps(OvnNorthboundTest):
def setUp(self):
super(TestAddressSetOps, self).setUp()
self.table = self.api.tables['Address_Set']
def _addr_set_add(self, name=None, *args, **kwargs):
if name is None:
name = utils.get_rand_name()
fix = self.useFixture(fixtures.AddressSetFixture(self.api, name,
*args, **kwargs))
self.assertIn(fix.obj.uuid, self.table.rows)
return fix.obj
def _test_addr_set_get(self, col):
addr_set = self._addr_set_add()
val = getattr(addr_set, col)
found = self.api.address_set_get(val).execute(check_error=True)
self.assertEqual(addr_set, found)
def test_addr_set_get_uuid(self):
self._test_addr_set_get('uuid')
def test_addr_set_get_name(self):
self._test_addr_set_get('name')
def test_addr_set_add_name(self):
name = utils.get_rand_device_name()
addr_set = self._addr_set_add(name)
self.assertEqual(name, addr_set.name)
def test_addr_set_add_exists(self):
name = utils.get_rand_device_name()
self._addr_set_add(name)
cmd = self.api.address_set_add(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_addr_set_add_may_exist(self):
name = utils.get_rand_device_name()
addr_set = self._addr_set_add(name)
addr_set2 = self.api.address_set_add(
name, may_exist=True).execute(check_error=True)
self.assertEqual(addr_set, addr_set2)
def test_addr_set_add_with_addresses(self):
addresses = ['192.168.0.1', '192.168.0.2']
addr_set = self._addr_set_add(addresses=addresses)
self.assertEqual(addresses, addr_set.addresses)
def test_addr_set_del(self):
addr_set = self._addr_set_add()
self.api.address_set_del(addr_set.uuid).execute(check_error=True)
self.assertNotIn(addr_set.uuid, self.table.rows)
def test_addr_set_del_by_name(self):
name = utils.get_rand_device_name()
self._addr_set_add(name)
self.api.address_set_del(name).execute(check_error=True)
def test_addr_set_del_no_exist(self):
name = utils.get_rand_device_name()
cmd = self.api.address_set_del(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_addr_set_del_if_exists(self):
name = utils.get_rand_device_name()
self.api.address_set_del(
name, if_exists=True).execute(check_error=True)
def test_addr_set_list(self):
addr_sets = {self._addr_set_add() for _ in range(3)}
found_sets = set(self.api.address_set_list().execute(check_error=True))
self.assertTrue(addr_sets.issubset(found_sets))
def test_addr_set_add_addresses(self):
addresses = ['192.168.0.1', '192.168.0.2']
addr_set = self._addr_set_add()
self.api.address_set_add_addresses(
addr_set.uuid, addresses).execute(check_error=True)
self.assertEqual(addresses, addr_set.addresses)
self.api.address_set_add_addresses(
addr_set.uuid, addresses).execute(check_error=True)
self.assertEqual(addresses, addr_set.addresses)
def test_addr_set_remove_addresses(self):
addresses = ['192.168.0.1', '192.168.0.2']
addr_set = self._addr_set_add(addresses=addresses)
self.api.address_set_remove_addresses(
addr_set.uuid, addresses).execute(check_error=True)
self.assertEqual(addr_set.addresses, [])
self.api.address_set_remove_addresses(
addr_set.uuid, addresses).execute(check_error=True)
self.assertEqual(addr_set.addresses, [])
def test_addr_set_add_remove_addresses_by_str(self):
address = "192.168.0.1"
addr_set = self._addr_set_add()
self.api.address_set_add_addresses(
addr_set.uuid, address).execute(check_error=True)
self.assertEqual([address], addr_set.addresses)
self.api.address_set_remove_addresses(
addr_set.uuid, address).execute(check_error=True)
self.assertEqual([], addr_set.addresses)
class TestQoSOps(OvnNorthboundTest):
def setUp(self):
super(TestQoSOps, self).setUp()

View File

@ -0,0 +1,4 @@
---
features:
- |
Added functions and commands to add, delete, list and update records of 'Address_Set' table.