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)
|
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):
|
def _add_sg_rule_acl_for_port_group(port_group, stateful, r):
|
||||||
# Update the match based on which direction this rule is for (ingress
|
# Update the match based on which direction this rule is for (ingress
|
||||||
# or egress).
|
# 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.
|
# Update the match if remote group id was specified.
|
||||||
match += acl_remote_group_id(r, ip_version)
|
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
|
# Update the match for the protocol (tcp, udp, icmp) and port/type
|
||||||
# range if specified.
|
# range if specified.
|
||||||
match += acl_protocol_and_ports(r, icmp)
|
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_HOST_ID_EXT_ID_KEY = 'neutron:host_id'
|
||||||
OVN_LRSR_EXT_ID_KEY = 'neutron:is_static_route'
|
OVN_LRSR_EXT_ID_KEY = 'neutron:is_static_route'
|
||||||
OVN_FIP_DISTRIBUTED_KEY = 'neutron:fip-distributed'
|
OVN_FIP_DISTRIBUTED_KEY = 'neutron:fip-distributed'
|
||||||
|
OVN_ADDRESS_GROUP_ID_KEY = 'neutron:address_group_id'
|
||||||
|
|
||||||
MIGRATING_ATTR = 'migrating_to'
|
MIGRATING_ATTR = 'migrating_to'
|
||||||
OVN_ROUTER_PORT_OPTION_KEYS = ['router-port', 'nat-addresses',
|
OVN_ROUTER_PORT_OPTION_KEYS = ['router-port', 'nat-addresses',
|
||||||
@ -263,6 +264,7 @@ TYPE_ROUTER_PORTS = 'router_ports'
|
|||||||
TYPE_SECURITY_GROUPS = 'security_groups'
|
TYPE_SECURITY_GROUPS = 'security_groups'
|
||||||
TYPE_FLOATINGIPS = 'floatingips'
|
TYPE_FLOATINGIPS = 'floatingips'
|
||||||
TYPE_SUBNETS = 'subnets'
|
TYPE_SUBNETS = 'subnets'
|
||||||
|
TYPE_ADDRESS_GROUPS = 'address_groups'
|
||||||
|
|
||||||
_TYPES_PRIORITY_ORDER = (
|
_TYPES_PRIORITY_ORDER = (
|
||||||
TYPE_NETWORKS,
|
TYPE_NETWORKS,
|
||||||
@ -272,6 +274,7 @@ _TYPES_PRIORITY_ORDER = (
|
|||||||
TYPE_PORTS,
|
TYPE_PORTS,
|
||||||
TYPE_ROUTER_PORTS,
|
TYPE_ROUTER_PORTS,
|
||||||
TYPE_FLOATINGIPS,
|
TYPE_FLOATINGIPS,
|
||||||
|
TYPE_ADDRESS_GROUPS,
|
||||||
TYPE_SECURITY_GROUP_RULES)
|
TYPE_SECURITY_GROUP_RULES)
|
||||||
|
|
||||||
DB_CONSISTENCY_CHECK_INTERVAL = 300 # 5 minutes
|
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('-', '_')
|
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):
|
def ovn_port_group_name(sg_id):
|
||||||
# The name of the port group for the given security group id.
|
# The name of the port group for the given security group id.
|
||||||
# The format is: pg-<security group uuid>.
|
# The format is: pg-<security group uuid>.
|
||||||
@ -549,6 +558,7 @@ def get_revision_number(resource, resource_type):
|
|||||||
constants.TYPE_ROUTERS,
|
constants.TYPE_ROUTERS,
|
||||||
constants.TYPE_ROUTER_PORTS,
|
constants.TYPE_ROUTER_PORTS,
|
||||||
constants.TYPE_SECURITY_GROUPS,
|
constants.TYPE_SECURITY_GROUPS,
|
||||||
|
constants.TYPE_ADDRESS_GROUPS,
|
||||||
constants.TYPE_FLOATINGIPS, constants.TYPE_SUBNETS):
|
constants.TYPE_FLOATINGIPS, constants.TYPE_SUBNETS):
|
||||||
return resource['revision_number']
|
return resource['revision_number']
|
||||||
else:
|
else:
|
||||||
|
@ -37,6 +37,7 @@ class AddressGroupDbMixin(ag_ext.AddressGroupPluginBase):
|
|||||||
res = address_group.to_dict()
|
res = address_group.to_dict()
|
||||||
res['addresses'] = [str(addr_assoc['address'])
|
res['addresses'] = [str(addr_assoc['address'])
|
||||||
for addr_assoc in address_group['addresses']]
|
for addr_assoc in address_group['addresses']]
|
||||||
|
res['standard_attr_id'] = address_group.standard_attr_id
|
||||||
return db_utils.resource_fields(res, fields)
|
return db_utils.resource_fields(res, fields)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -44,6 +44,7 @@ TYPE_ROUTER_PORTS = 'router_ports'
|
|||||||
TYPE_SECURITY_GROUPS = 'security_groups'
|
TYPE_SECURITY_GROUPS = 'security_groups'
|
||||||
TYPE_FLOATINGIPS = 'floatingips'
|
TYPE_FLOATINGIPS = 'floatingips'
|
||||||
TYPE_SUBNETS = 'subnets'
|
TYPE_SUBNETS = 'subnets'
|
||||||
|
TYPE_ADDRESS_GROUPS = 'address_groups'
|
||||||
|
|
||||||
_TYPES_PRIORITY_ORDER = (
|
_TYPES_PRIORITY_ORDER = (
|
||||||
TYPE_NETWORKS,
|
TYPE_NETWORKS,
|
||||||
@ -53,6 +54,7 @@ _TYPES_PRIORITY_ORDER = (
|
|||||||
TYPE_PORTS,
|
TYPE_PORTS,
|
||||||
TYPE_ROUTER_PORTS,
|
TYPE_ROUTER_PORTS,
|
||||||
TYPE_FLOATINGIPS,
|
TYPE_FLOATINGIPS,
|
||||||
|
TYPE_ADDRESS_GROUPS,
|
||||||
TYPE_SECURITY_GROUP_RULES)
|
TYPE_SECURITY_GROUP_RULES)
|
||||||
|
|
||||||
# The order in which the resources should be created or updated by the
|
# The order in which the resources should be created or updated by the
|
||||||
|
@ -253,7 +253,7 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
resources.SEGMENT,
|
resources.SEGMENT,
|
||||||
events.AFTER_DELETE)
|
events.AFTER_DELETE)
|
||||||
|
|
||||||
# Handle security group/rule notifications
|
# Handle security group/rule or address group notifications
|
||||||
if self.sg_enabled:
|
if self.sg_enabled:
|
||||||
registry.subscribe(self._create_security_group_precommit,
|
registry.subscribe(self._create_security_group_precommit,
|
||||||
resources.SECURITY_GROUP,
|
resources.SECURITY_GROUP,
|
||||||
@ -279,6 +279,15 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
registry.subscribe(self._process_sg_rule_notification,
|
registry.subscribe(self._process_sg_rule_notification,
|
||||||
resources.SECURITY_GROUP_RULE,
|
resources.SECURITY_GROUP_RULE,
|
||||||
events.BEFORE_DELETE)
|
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):
|
def _remove_node_from_hash_ring(self, *args, **kwargs):
|
||||||
# The node_uuid attribute will be empty for worker types
|
# The node_uuid attribute will be empty for worker types
|
||||||
@ -488,6 +497,25 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
return True
|
return True
|
||||||
return False
|
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):
|
def _is_network_type_supported(self, network_type):
|
||||||
return (network_type in [const.TYPE_LOCAL,
|
return (network_type in [const.TYPE_LOCAL,
|
||||||
const.TYPE_FLAT,
|
const.TYPE_FLAT,
|
||||||
|
@ -36,6 +36,7 @@ RESOURCE_TYPE_MAP = {
|
|||||||
ovn_const.TYPE_ROUTER_PORTS: 'Logical_Router_Port',
|
ovn_const.TYPE_ROUTER_PORTS: 'Logical_Router_Port',
|
||||||
ovn_const.TYPE_FLOATINGIPS: 'NAT',
|
ovn_const.TYPE_FLOATINGIPS: 'NAT',
|
||||||
ovn_const.TYPE_SUBNETS: 'DHCP_Options',
|
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)
|
pg_name = utils.ovn_port_group_name(pg_name)
|
||||||
return self.lookup('Port_Group', pg_name, default=None)
|
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):
|
def get_sg_port_groups(self):
|
||||||
"""Returns OVN port groups used as Neutron Security Groups.
|
"""Returns OVN port groups used as Neutron Security Groups.
|
||||||
|
|
||||||
|
@ -206,6 +206,13 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
|||||||
'ovn_update': self._ovn_client.update_router,
|
'ovn_update': self._ovn_client.update_router,
|
||||||
'ovn_delete': self._ovn_client.delete_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: {
|
ovn_const.TYPE_SECURITY_GROUPS: {
|
||||||
'neutron_get': self._ovn_client._plugin.get_security_group,
|
'neutron_get': self._ovn_client._plugin.get_security_group,
|
||||||
'ovn_get': self._nb_idl.get_port_group,
|
'ovn_get': self._nb_idl.get_port_group,
|
||||||
@ -275,6 +282,29 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
|||||||
# supposed to be.
|
# supposed to be.
|
||||||
revision_numbers_db.bump_revision(context, n_obj,
|
revision_numbers_db.bump_revision(context, n_obj,
|
||||||
row.resource_type)
|
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:
|
else:
|
||||||
ext_ids = getattr(ovn_obj, 'external_ids', {})
|
ext_ids = getattr(ovn_obj, 'external_ids', {})
|
||||||
ovn_revision = int(ext_ids.get(
|
ovn_revision = int(ext_ids.get(
|
||||||
|
@ -2574,6 +2574,84 @@ class OVNClient(object):
|
|||||||
db_rev.delete_revision(
|
db_rev.delete_revision(
|
||||||
context, rule['id'], ovn_const.TYPE_SECURITY_GROUP_RULES)
|
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):
|
def _find_metadata_port(self, context, network_id):
|
||||||
if not ovn_conf.is_ovn_metadata_enabled():
|
if not ovn_conf.is_ovn_metadata_enabled():
|
||||||
return
|
return
|
||||||
|
@ -180,3 +180,44 @@ class TestOVNClient(base.TestOVNFunctionalBase,
|
|||||||
|
|
||||||
def test_router_reside_chassis_redirect_non_dvr_geneve_net(self):
|
def test_router_reside_chassis_redirect_non_dvr_geneve_net(self):
|
||||||
self._test_router_reside_chassis_redirect(False, 'geneve')
|
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
|
import neutron.extensions
|
||||||
from neutron.services.revisions import revision_plugin
|
from neutron.services.revisions import revision_plugin
|
||||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
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_l3
|
||||||
from neutron.tests.unit.extensions import test_securitygroup
|
from neutron.tests.unit.extensions import test_securitygroup
|
||||||
|
|
||||||
@ -150,6 +151,7 @@ class TestExtensionManager(extensions.PluginAwareExtensionManager):
|
|||||||
|
|
||||||
|
|
||||||
class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
||||||
|
test_address_group.AddressGroupTestCase,
|
||||||
test_l3.L3NatTestCaseMixin):
|
test_l3.L3NatTestCaseMixin):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -246,6 +248,9 @@ class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
|||||||
sg['id'], 'ingress', n_const.PROTO_NUM_TCP)
|
sg['id'], 'ingress', n_const.PROTO_NUM_TCP)
|
||||||
sg_rule = self._make_security_group_rule(
|
sg_rule = self._make_security_group_rule(
|
||||||
self.fmt, rule)['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(router['id'], ovn_rn_db.TYPE_ROUTERS)
|
||||||
self._create_initial_revision(subnet['id'], ovn_rn_db.TYPE_SUBNETS)
|
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'],
|
self._create_initial_revision(sg_rule['id'],
|
||||||
ovn_rn_db.TYPE_SECURITY_GROUP_RULES)
|
ovn_rn_db.TYPE_SECURITY_GROUP_RULES)
|
||||||
self._create_initial_revision(self.net['id'], ovn_rn_db.TYPE_NETWORKS)
|
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:
|
if delete:
|
||||||
self._delete('security-group-rules', sg_rule['id'])
|
self._delete('security-group-rules', sg_rule['id'])
|
||||||
@ -265,6 +271,7 @@ class TestRevisionNumberMaintenance(test_securitygroup.SecurityGroupsTestCase,
|
|||||||
self._delete('routers', router['id'])
|
self._delete('routers', router['id'])
|
||||||
self._delete('subnets', subnet['id'])
|
self._delete('subnets', subnet['id'])
|
||||||
self._delete('networks', self.net['id'])
|
self._delete('networks', self.net['id'])
|
||||||
|
self._delete('address-groups', ag['id'])
|
||||||
|
|
||||||
def test_get_inconsistent_resources_order(self):
|
def test_get_inconsistent_resources_order(self):
|
||||||
self._prepare_resources_for_ordering_test()
|
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.db import ovn_revision_numbers_db
|
||||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
|
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
|
||||||
from neutron.tests import base
|
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 import fake_resources as fakes
|
||||||
from neutron.tests.unit.plugins.ml2 import test_security_group as test_sg
|
from neutron.tests.unit.plugins.ml2 import test_security_group as test_sg
|
||||||
from neutron.tests.unit import testlib_api
|
from neutron.tests.unit import testlib_api
|
||||||
@ -139,7 +140,8 @@ class TestSchemaAwarePeriodicsBase(testlib_api.SqlTestCaseLight):
|
|||||||
@mock.patch.object(maintenance.DBInconsistenciesPeriodics,
|
@mock.patch.object(maintenance.DBInconsistenciesPeriodics,
|
||||||
'has_lock', mock.PropertyMock(return_value=True))
|
'has_lock', mock.PropertyMock(return_value=True))
|
||||||
class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
||||||
test_sg.Ml2SecurityGroupsTestCase):
|
test_sg.Ml2SecurityGroupsTestCase,
|
||||||
|
test_ag.AddressGroupTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
ovn_conf.register_opts()
|
ovn_conf.register_opts()
|
||||||
@ -148,6 +150,9 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
|||||||
self.fmt, name='net1', admin_state_up=True)['network']
|
self.fmt, name='net1', admin_state_up=True)['network']
|
||||||
self.port = self._make_port(
|
self.port = self._make_port(
|
||||||
self.fmt, self.net['id'], name='port1')['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.fake_ovn_client = mock.MagicMock()
|
||||||
self.periodic = maintenance.DBInconsistenciesPeriodics(
|
self.periodic = maintenance.DBInconsistenciesPeriodics(
|
||||||
self.fake_ovn_client)
|
self.fake_ovn_client)
|
||||||
@ -286,6 +291,52 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
|||||||
def test_fix_security_group_create_version_mismatch(self):
|
def test_fix_security_group_create_version_mismatch(self):
|
||||||
self._test_fix_security_group_create(revision_number=2)
|
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')
|
@mock.patch.object(maintenance, 'LOG')
|
||||||
def test__fix_create_update_no_sttd_attr(self, mock_log):
|
def test__fix_create_update_no_sttd_attr(self, mock_log):
|
||||||
row_net = ovn_models.OVNRevisionNumbers(
|
row_net = ovn_models.OVNRevisionNumbers(
|
||||||
|
@ -237,6 +237,12 @@ class TestOVNClient(TestOVNClientBase):
|
|||||||
self.ovn_client._add_router_ext_gw(mock.Mock(), router, txn))
|
self.ovn_client._add_router_ext_gw(mock.Mock(), router, txn))
|
||||||
self.nb_idl.add_static_route.assert_not_called()
|
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):
|
def test_update_lsp_host_info_up(self):
|
||||||
context = mock.MagicMock()
|
context = mock.MagicMock()
|
||||||
host_id = 'fake-binding-host-id'
|
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