Merge "[OVN] Support address group for ovn driver"
This commit is contained in:
commit
1df5dbd9b6
@ -233,6 +233,16 @@ def acl_remote_group_id(r, ip_version):
|
||||
return ' && %s.%s == $%s' % (ip_version, src_or_dst, addrset_name)
|
||||
|
||||
|
||||
def acl_remote_address_group_id(r, ip_version):
|
||||
if not r.get('remote_address_group_id'):
|
||||
return ''
|
||||
|
||||
src_or_dst = 'src' if r['direction'] == const.INGRESS_DIRECTION else 'dst'
|
||||
addrset_name = utils.ovn_ag_addrset_name(r['remote_address_group_id'],
|
||||
ip_version)
|
||||
return ' && %s.%s == $%s' % (ip_version, src_or_dst, addrset_name)
|
||||
|
||||
|
||||
def _add_sg_rule_acl_for_port_group(port_group, stateful, r):
|
||||
# Update the match based on which direction this rule is for (ingress
|
||||
# or egress).
|
||||
@ -248,6 +258,9 @@ def _add_sg_rule_acl_for_port_group(port_group, stateful, r):
|
||||
# Update the match if remote group id was specified.
|
||||
match += acl_remote_group_id(r, ip_version)
|
||||
|
||||
# Update the match if remote address group id was specified.
|
||||
match += acl_remote_address_group_id(r, ip_version)
|
||||
|
||||
# Update the match for the protocol (tcp, udp, icmp) and port/type
|
||||
# range if specified.
|
||||
match += acl_protocol_and_ports(r, icmp)
|
||||
|
@ -56,6 +56,7 @@ OVN_PORT_BINDING_PROFILE = portbindings.PROFILE
|
||||
OVN_HOST_ID_EXT_ID_KEY = 'neutron:host_id'
|
||||
OVN_LRSR_EXT_ID_KEY = 'neutron:is_static_route'
|
||||
OVN_FIP_DISTRIBUTED_KEY = 'neutron:fip-distributed'
|
||||
OVN_ADDRESS_GROUP_ID_KEY = 'neutron:address_group_id'
|
||||
|
||||
MIGRATING_ATTR = 'migrating_to'
|
||||
OVN_ROUTER_PORT_OPTION_KEYS = ['router-port', 'nat-addresses',
|
||||
@ -263,6 +264,7 @@ TYPE_ROUTER_PORTS = 'router_ports'
|
||||
TYPE_SECURITY_GROUPS = 'security_groups'
|
||||
TYPE_FLOATINGIPS = 'floatingips'
|
||||
TYPE_SUBNETS = 'subnets'
|
||||
TYPE_ADDRESS_GROUPS = 'address_groups'
|
||||
|
||||
_TYPES_PRIORITY_ORDER = (
|
||||
TYPE_NETWORKS,
|
||||
@ -272,6 +274,7 @@ _TYPES_PRIORITY_ORDER = (
|
||||
TYPE_PORTS,
|
||||
TYPE_ROUTER_PORTS,
|
||||
TYPE_FLOATINGIPS,
|
||||
TYPE_ADDRESS_GROUPS,
|
||||
TYPE_SECURITY_GROUP_RULES)
|
||||
|
||||
DB_CONSISTENCY_CHECK_INTERVAL = 300 # 5 minutes
|
||||
|
@ -261,6 +261,15 @@ def ovn_pg_addrset_name(sg_id, ip_version):
|
||||
return ('pg-%s-%s' % (sg_id, ip_version)).replace('-', '_')
|
||||
|
||||
|
||||
def ovn_ag_addrset_name(ag_id, ip_version):
|
||||
# The name of the address set for the given address group id and ip
|
||||
# version. The format is:
|
||||
# ag-<address group uuid>-<ip version>
|
||||
# with all '-' replaced with '_'. This replacement is necessary
|
||||
# because OVN doesn't support '-' in an address set name.
|
||||
return ('ag-%s-%s' % (ag_id, ip_version)).replace('-', '_')
|
||||
|
||||
|
||||
def ovn_port_group_name(sg_id):
|
||||
# The name of the port group for the given security group id.
|
||||
# The format is: pg-<security group uuid>.
|
||||
@ -549,6 +558,7 @@ def get_revision_number(resource, resource_type):
|
||||
constants.TYPE_ROUTERS,
|
||||
constants.TYPE_ROUTER_PORTS,
|
||||
constants.TYPE_SECURITY_GROUPS,
|
||||
constants.TYPE_ADDRESS_GROUPS,
|
||||
constants.TYPE_FLOATINGIPS, constants.TYPE_SUBNETS):
|
||||
return resource['revision_number']
|
||||
else:
|
||||
|
@ -37,6 +37,7 @@ class AddressGroupDbMixin(ag_ext.AddressGroupPluginBase):
|
||||
res = address_group.to_dict()
|
||||
res['addresses'] = [str(addr_assoc['address'])
|
||||
for addr_assoc in address_group['addresses']]
|
||||
res['standard_attr_id'] = address_group.standard_attr_id
|
||||
return db_utils.resource_fields(res, fields)
|
||||
|
||||
@staticmethod
|
||||
|
@ -44,6 +44,7 @@ TYPE_ROUTER_PORTS = 'router_ports'
|
||||
TYPE_SECURITY_GROUPS = 'security_groups'
|
||||
TYPE_FLOATINGIPS = 'floatingips'
|
||||
TYPE_SUBNETS = 'subnets'
|
||||
TYPE_ADDRESS_GROUPS = 'address_groups'
|
||||
|
||||
_TYPES_PRIORITY_ORDER = (
|
||||
TYPE_NETWORKS,
|
||||
@ -53,6 +54,7 @@ _TYPES_PRIORITY_ORDER = (
|
||||
TYPE_PORTS,
|
||||
TYPE_ROUTER_PORTS,
|
||||
TYPE_FLOATINGIPS,
|
||||
TYPE_ADDRESS_GROUPS,
|
||||
TYPE_SECURITY_GROUP_RULES)
|
||||
|
||||
# The order in which the resources should be created or updated by the
|
||||
|
@ -253,7 +253,7 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||
resources.SEGMENT,
|
||||
events.AFTER_DELETE)
|
||||
|
||||
# Handle security group/rule notifications
|
||||
# Handle security group/rule or address group notifications
|
||||
if self.sg_enabled:
|
||||
registry.subscribe(self._create_security_group_precommit,
|
||||
resources.SECURITY_GROUP,
|
||||
@ -279,6 +279,15 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||
registry.subscribe(self._process_sg_rule_notification,
|
||||
resources.SECURITY_GROUP_RULE,
|
||||
events.BEFORE_DELETE)
|
||||
registry.subscribe(self._process_ag_notification,
|
||||
resources.ADDRESS_GROUP,
|
||||
events.AFTER_CREATE)
|
||||
registry.subscribe(self._process_ag_notification,
|
||||
resources.ADDRESS_GROUP,
|
||||
events.AFTER_UPDATE)
|
||||
registry.subscribe(self._process_ag_notification,
|
||||
resources.ADDRESS_GROUP,
|
||||
events.AFTER_DELETE)
|
||||
|
||||
def _remove_node_from_hash_ring(self, *args, **kwargs):
|
||||
# The node_uuid attribute will be empty for worker types
|
||||
@ -488,6 +497,25 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _process_ag_notification(
|
||||
self, resource, event, trigger, payload):
|
||||
context = payload.context
|
||||
address_group = payload.latest_state
|
||||
address_group_id = payload.resource_id
|
||||
if event == events.AFTER_CREATE:
|
||||
ovn_revision_numbers_db.create_initial_revision(
|
||||
context, address_group_id, ovn_const.TYPE_ADDRESS_GROUPS,
|
||||
std_attr_id=address_group['standard_attr_id'])
|
||||
self._ovn_client.create_address_group(
|
||||
context, address_group)
|
||||
elif event == events.AFTER_UPDATE:
|
||||
self._ovn_client.update_address_group(
|
||||
context, address_group)
|
||||
elif event == events.AFTER_DELETE:
|
||||
self._ovn_client.delete_address_group(
|
||||
context,
|
||||
address_group_id)
|
||||
|
||||
def _is_network_type_supported(self, network_type):
|
||||
return (network_type in [const.TYPE_LOCAL,
|
||||
const.TYPE_FLAT,
|
||||
|
@ -36,6 +36,7 @@ RESOURCE_TYPE_MAP = {
|
||||
ovn_const.TYPE_ROUTER_PORTS: 'Logical_Router_Port',
|
||||
ovn_const.TYPE_FLOATINGIPS: 'NAT',
|
||||
ovn_const.TYPE_SUBNETS: 'DHCP_Options',
|
||||
ovn_const.TYPE_ADDRESS_GROUPS: 'Address_Set',
|
||||
}
|
||||
|
||||
|
||||
|
@ -859,6 +859,14 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend):
|
||||
pg_name = utils.ovn_port_group_name(pg_name)
|
||||
return self.lookup('Port_Group', pg_name, default=None)
|
||||
|
||||
def get_address_set(self, as_name):
|
||||
if uuidutils.is_uuid_like(as_name):
|
||||
as_name_v4 = utils.ovn_ag_addrset_name(as_name, 'ip4')
|
||||
as_name_v6 = utils.ovn_ag_addrset_name(as_name, 'ip6')
|
||||
return (self.lookup('Address_Set', as_name_v4, default=None),
|
||||
self.lookup('Address_Set', as_name_v6, default=None))
|
||||
return self.lookup('Address_Set', as_name, default=None), None
|
||||
|
||||
def get_sg_port_groups(self):
|
||||
"""Returns OVN port groups used as Neutron Security Groups.
|
||||
|
||||
|
@ -206,6 +206,13 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
||||
'ovn_update': self._ovn_client.update_router,
|
||||
'ovn_delete': self._ovn_client.delete_router,
|
||||
},
|
||||
ovn_const.TYPE_ADDRESS_GROUPS: {
|
||||
'neutron_get': self._ovn_client._plugin.get_address_group,
|
||||
'ovn_get': self._nb_idl.get_address_set,
|
||||
'ovn_create': self._ovn_client.create_address_group,
|
||||
'ovn_update': self._ovn_client.update_address_group,
|
||||
'ovn_delete': self._ovn_client.delete_address_group,
|
||||
},
|
||||
ovn_const.TYPE_SECURITY_GROUPS: {
|
||||
'neutron_get': self._ovn_client._plugin.get_security_group,
|
||||
'ovn_get': self._nb_idl.get_port_group,
|
||||
@ -275,6 +282,29 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
||||
# supposed to be.
|
||||
revision_numbers_db.bump_revision(context, n_obj,
|
||||
row.resource_type)
|
||||
elif row.resource_type == ovn_const.TYPE_ADDRESS_GROUPS:
|
||||
need_bump = False
|
||||
for obj in ovn_obj:
|
||||
if not obj:
|
||||
# NOTE(liushy): We create two Address_Sets for
|
||||
# one Address_Group at one ovn_create func.
|
||||
res_map['ovn_create'](context, n_obj)
|
||||
need_bump = False
|
||||
break
|
||||
ext_ids = getattr(obj, 'external_ids', {})
|
||||
ovn_revision = int(ext_ids.get(
|
||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY, -1))
|
||||
# NOTE(liushy): We have created two Address_Sets
|
||||
# for one Address_Group, and we update both of
|
||||
# them at one ovn_update func.
|
||||
if ovn_revision != n_obj['revision_number']:
|
||||
res_map['ovn_update'](context, n_obj)
|
||||
need_bump = False
|
||||
break
|
||||
need_bump = True
|
||||
if need_bump:
|
||||
revision_numbers_db.bump_revision(context, n_obj,
|
||||
row.resource_type)
|
||||
else:
|
||||
ext_ids = getattr(ovn_obj, 'external_ids', {})
|
||||
ovn_revision = int(ext_ids.get(
|
||||
|
@ -2574,6 +2574,84 @@ class OVNClient(object):
|
||||
db_rev.delete_revision(
|
||||
context, rule['id'], ovn_const.TYPE_SECURITY_GROUP_RULES)
|
||||
|
||||
def _checkout_ip_list(self, addresses):
|
||||
"""Return address map for addresses.
|
||||
|
||||
This method will check out ipv4 and ipv6 address list from the
|
||||
given address list.
|
||||
Eg. if addresses = ["192.168.2.2/32", "2001:db8::/32"], it will
|
||||
return {"4":["192.168.2.2/32"], "6":["2001:db8::/32"]}.
|
||||
|
||||
:param addresses: address list.
|
||||
"""
|
||||
if not addresses:
|
||||
addresses = []
|
||||
ip_addresses = [netaddr.IPNetwork(ip)
|
||||
for ip in addresses]
|
||||
addr_map = {const.IP_VERSION_4: [], const.IP_VERSION_6: []}
|
||||
for addr in ip_addresses:
|
||||
addr_map[addr.version].append(str(addr.cidr))
|
||||
return addr_map
|
||||
|
||||
def create_address_group(self, context, address_group):
|
||||
addr_map_all = self._checkout_ip_list(
|
||||
address_group.get('addresses'))
|
||||
external_ids = {ovn_const.OVN_ADDRESS_GROUP_ID_KEY:
|
||||
address_group['id'],
|
||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: str(
|
||||
utils.get_revision_number(
|
||||
address_group,
|
||||
ovn_const.TYPE_ADDRESS_GROUPS))
|
||||
}
|
||||
attrs = [('external_ids', external_ids),]
|
||||
for ip_version in const.IP_ALLOWED_VERSIONS:
|
||||
as_name = utils.ovn_ag_addrset_name(address_group['id'],
|
||||
'ip' + str(ip_version))
|
||||
with self._nb_idl.transaction(check_error=True) as txn:
|
||||
txn.add(self._nb_idl.address_set_add(
|
||||
as_name, addresses=addr_map_all[ip_version],
|
||||
may_exist=True))
|
||||
txn.add(self._nb_idl.db_set(
|
||||
'Address_Set', as_name, *attrs))
|
||||
db_rev.bump_revision(
|
||||
context, address_group, ovn_const.TYPE_ADDRESS_GROUPS)
|
||||
|
||||
def update_address_group(self, context, address_group):
|
||||
addr_map_db = self._checkout_ip_list(address_group['addresses'])
|
||||
for ip_version in const.IP_ALLOWED_VERSIONS:
|
||||
as_name = utils.ovn_ag_addrset_name(address_group['id'],
|
||||
'ip' + str(ip_version))
|
||||
check_rev_cmd = self._nb_idl.check_revision_number(
|
||||
as_name, address_group, ovn_const.TYPE_ADDRESS_GROUPS)
|
||||
with self._nb_idl.transaction(check_error=True) as txn:
|
||||
txn.add(check_rev_cmd)
|
||||
# For add/remove addresses
|
||||
addr_ovn = self._nb_idl.get_address_set(as_name)[0].addresses
|
||||
added = set(addr_map_db[ip_version]) - set(addr_ovn)
|
||||
removed = set(addr_ovn) - set(addr_map_db[ip_version])
|
||||
txn.add(self._nb_idl.address_set_add_addresses(
|
||||
as_name,
|
||||
added
|
||||
))
|
||||
txn.add(self._nb_idl.address_set_remove_addresses(
|
||||
as_name,
|
||||
removed
|
||||
))
|
||||
if check_rev_cmd.result == ovn_const.TXN_COMMITTED:
|
||||
db_rev.bump_revision(
|
||||
context, address_group, ovn_const.TYPE_ADDRESS_GROUPS)
|
||||
|
||||
def delete_address_group(self, context, address_group_id):
|
||||
ipv4_as_name = utils.ovn_ag_addrset_name(address_group_id, 'ip4')
|
||||
ipv6_as_name = utils.ovn_ag_addrset_name(address_group_id, 'ip6')
|
||||
with self._nb_idl.transaction(check_error=True) as txn:
|
||||
txn.add(self._nb_idl.address_set_del(
|
||||
ipv4_as_name, if_exists=True))
|
||||
txn.add(self._nb_idl.address_set_del(
|
||||
ipv6_as_name, if_exists=True))
|
||||
db_rev.delete_revision(
|
||||
context, address_group_id, ovn_const.TYPE_ADDRESS_GROUPS)
|
||||
|
||||
def _find_metadata_port(self, context, network_id):
|
||||
if not ovn_conf.is_ovn_metadata_enabled():
|
||||
return
|
||||
|
@ -180,3 +180,44 @@ class TestOVNClient(base.TestOVNFunctionalBase,
|
||||
|
||||
def test_router_reside_chassis_redirect_non_dvr_geneve_net(self):
|
||||
self._test_router_reside_chassis_redirect(False, 'geneve')
|
||||
|
||||
def test_process_address_group(self):
|
||||
def _find_address_set_for_ag():
|
||||
as_v4 = self.nb_api.lookup(
|
||||
'Address_Set',
|
||||
ovn_utils.ovn_ag_addrset_name(
|
||||
ag['id'], 'ip' + str(constants.IP_VERSION_4)),
|
||||
default=None)
|
||||
as_v6 = self.nb_api.lookup(
|
||||
'Address_Set',
|
||||
ovn_utils.ovn_ag_addrset_name(
|
||||
ag['id'], 'ip' + str(constants.IP_VERSION_6)),
|
||||
default=None)
|
||||
return as_v4, as_v6
|
||||
|
||||
ovn_client = self.mech_driver._ovn_client
|
||||
ag_args = {'project_id': 'project_1',
|
||||
'name': 'test_address_group',
|
||||
'description': 'test address group',
|
||||
'addresses': ['192.168.2.2/32',
|
||||
'2001:db8::/32']}
|
||||
ag = self.plugin.create_address_group(self.context,
|
||||
{'address_group': ag_args})
|
||||
self.assertIsNotNone(_find_address_set_for_ag()[0])
|
||||
self.assertIsNotNone(_find_address_set_for_ag()[1])
|
||||
|
||||
# Call the create_address_group again to ensure that the create
|
||||
# command automatically checks for existing Address_Set
|
||||
ovn_client.create_address_group(self.context, ag)
|
||||
|
||||
# Update the address group
|
||||
ag['addresses'] = ['20.0.0.1/32', '2002:db8::/32']
|
||||
ovn_client.update_address_group(self.context, ag)
|
||||
as_v4_new = _find_address_set_for_ag()[0]
|
||||
as_v6_new = _find_address_set_for_ag()[1]
|
||||
self.assertEqual(['20.0.0.1/32'], as_v4_new.addresses)
|
||||
self.assertEqual(['2002:db8::/32'], as_v6_new.addresses)
|
||||
|
||||
# Delete the address group
|
||||
ovn_client.delete_address_group(self.context, ag['id'])
|
||||
self.assertEqual((None, None), _find_address_set_for_ag())
|
||||
|
@ -29,6 +29,7 @@ from neutron.db import ovn_revision_numbers_db as ovn_rn_db
|
||||
import neutron.extensions
|
||||
from neutron.services.revisions import revision_plugin
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
||||
from neutron.tests.unit.extensions import test_address_group
|
||||
from neutron.tests.unit.extensions import test_l3
|
||||
from neutron.tests.unit.extensions import test_securitygroup
|
||||
|
||||
@ -150,6 +151,7 @@ class TestExtensionManager(extensions.PluginAwareExtensionManager):
|
||||
|
||||
|
||||
class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
||||
test_address_group.AddressGroupTestCase,
|
||||
test_l3.L3NatTestCaseMixin):
|
||||
|
||||
def setUp(self):
|
||||
@ -246,6 +248,9 @@ class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
||||
sg['id'], 'ingress', n_const.PROTO_NUM_TCP)
|
||||
sg_rule = self._make_security_group_rule(
|
||||
self.fmt, rule)['security_group_rule']
|
||||
ag = self.deserialize(
|
||||
self.fmt, self._create_address_group(
|
||||
**{'name': 'ag1'}))['address_group']
|
||||
|
||||
self._create_initial_revision(router['id'], ovn_rn_db.TYPE_ROUTERS)
|
||||
self._create_initial_revision(subnet['id'], ovn_rn_db.TYPE_SUBNETS)
|
||||
@ -256,6 +261,7 @@ class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
||||
self._create_initial_revision(sg_rule['id'],
|
||||
ovn_rn_db.TYPE_SECURITY_GROUP_RULES)
|
||||
self._create_initial_revision(self.net['id'], ovn_rn_db.TYPE_NETWORKS)
|
||||
self._create_initial_revision(ag['id'], ovn_rn_db.TYPE_ADDRESS_GROUPS)
|
||||
|
||||
if delete:
|
||||
self._delete('security-group-rules', sg_rule['id'])
|
||||
@ -265,6 +271,7 @@ class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
||||
self._delete('routers', router['id'])
|
||||
self._delete('subnets', subnet['id'])
|
||||
self._delete('networks', self.net['id'])
|
||||
self._delete('address-groups', ag['id'])
|
||||
|
||||
def test_get_inconsistent_resources_order(self):
|
||||
self._prepare_resources_for_ordering_test()
|
||||
|
@ -30,6 +30,7 @@ from neutron.db.models import ovn as ovn_models
|
||||
from neutron.db import ovn_revision_numbers_db
|
||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit.extensions import test_address_group as test_ag
|
||||
from neutron.tests.unit import fake_resources as fakes
|
||||
from neutron.tests.unit.plugins.ml2 import test_security_group as test_sg
|
||||
from neutron.tests.unit import testlib_api
|
||||
@ -139,7 +140,8 @@ class TestSchemaAwarePeriodicsBase(testlib_api.SqlTestCaseLight):
|
||||
@mock.patch.object(maintenance.DBInconsistenciesPeriodics,
|
||||
'has_lock', mock.PropertyMock(return_value=True))
|
||||
class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
||||
test_sg.Ml2SecurityGroupsTestCase):
|
||||
test_sg.Ml2SecurityGroupsTestCase,
|
||||
test_ag.AddressGroupTestCase):
|
||||
|
||||
def setUp(self):
|
||||
ovn_conf.register_opts()
|
||||
@ -148,6 +150,9 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
||||
self.fmt, name='net1', admin_state_up=True)['network']
|
||||
self.port = self._make_port(
|
||||
self.fmt, self.net['id'], name='port1')['port']
|
||||
self.ag = self.deserialize(
|
||||
self.fmt, self._create_address_group(
|
||||
**{'name': 'ag1'}))['address_group']
|
||||
self.fake_ovn_client = mock.MagicMock()
|
||||
self.periodic = maintenance.DBInconsistenciesPeriodics(
|
||||
self.fake_ovn_client)
|
||||
@ -286,6 +291,52 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
||||
def test_fix_security_group_create_version_mismatch(self):
|
||||
self._test_fix_security_group_create(revision_number=2)
|
||||
|
||||
def _test_fix_create_update_address_group(self, ovn_rev, neutron_rev):
|
||||
_nb_idl = self.fake_ovn_client._nb_idl
|
||||
with db_api.CONTEXT_WRITER.using(self.ctx):
|
||||
self.ag['revision_number'] = neutron_rev
|
||||
|
||||
# Create an entry to the revision_numbers table and assert the
|
||||
# initial revision_number for our test object is the expected
|
||||
ovn_revision_numbers_db.create_initial_revision(
|
||||
self.ctx, self.ag['id'], constants.TYPE_ADDRESS_GROUPS,
|
||||
revision_number=ovn_rev)
|
||||
row = ovn_revision_numbers_db.get_revision_row(self.ctx,
|
||||
self.ag['id'])
|
||||
self.assertEqual(ovn_rev, row.revision_number)
|
||||
|
||||
if ovn_rev < 0:
|
||||
_nb_idl.get_address_set.return_value = None, None
|
||||
else:
|
||||
fake_as_v4 = mock.Mock(external_ids={
|
||||
constants.OVN_REV_NUM_EXT_ID_KEY: ovn_rev})
|
||||
fake_as_v6 = mock.Mock(external_ids={
|
||||
constants.OVN_REV_NUM_EXT_ID_KEY: ovn_rev})
|
||||
_nb_idl.get_address_set.return_value = fake_as_v4, fake_as_v6
|
||||
|
||||
self.fake_ovn_client._plugin.get_address_group.return_value = \
|
||||
self.ag
|
||||
self.periodic._fix_create_update(self.ctx, row)
|
||||
|
||||
# Since the revision number was < 0, make sure
|
||||
# create_address_group() is invoked with the latest
|
||||
# version of the object in the neutron database
|
||||
if ovn_rev < 0:
|
||||
self.fake_ovn_client.create_address_group.\
|
||||
assert_called_once_with(self.ctx, self.ag)
|
||||
# If the revision number is > 0 it means that the object already
|
||||
# exist and we just need to update to match the latest in the
|
||||
# neutron database so, update_address_group() should be called.
|
||||
else:
|
||||
self.fake_ovn_client.update_address_group.\
|
||||
assert_called_once_with(self.ctx, self.ag)
|
||||
|
||||
def test_fix_address_group_create(self):
|
||||
self._test_fix_create_update_address_group(ovn_rev=-1, neutron_rev=2)
|
||||
|
||||
def test_fix_address_group_update(self):
|
||||
self._test_fix_create_update_address_group(ovn_rev=5, neutron_rev=7)
|
||||
|
||||
@mock.patch.object(maintenance, 'LOG')
|
||||
def test__fix_create_update_no_sttd_attr(self, mock_log):
|
||||
row_net = ovn_models.OVNRevisionNumbers(
|
||||
|
@ -237,6 +237,12 @@ class TestOVNClient(TestOVNClientBase):
|
||||
self.ovn_client._add_router_ext_gw(mock.Mock(), router, txn))
|
||||
self.nb_idl.add_static_route.assert_not_called()
|
||||
|
||||
def test_checkout_ip_list(self):
|
||||
addresses = ["192.168.2.2/32", "2001:db8::/32"]
|
||||
add_map = self.ovn_client._checkout_ip_list(addresses)
|
||||
self.assertEqual(["192.168.2.2/32"], add_map[const.IP_VERSION_4])
|
||||
self.assertEqual(["2001:db8::/32"], add_map[const.IP_VERSION_6])
|
||||
|
||||
def test_update_lsp_host_info_up(self):
|
||||
context = mock.MagicMock()
|
||||
host_id = 'fake-binding-host-id'
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add support for the ``address-group`` in the OVN mechanism driver.
|
Loading…
Reference in New Issue
Block a user