Skip to content

Commit 8c69f74

Browse files
authored
Merge pull request #100 from stackhpc/upstream/zed-2023-12-18
Synchronise zed with upstream
2 parents f2e224a + 049fc36 commit 8c69f74

File tree

4 files changed

+235
-14
lines changed

4 files changed

+235
-14
lines changed

neutron/db/db_base_plugin_v2.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def _validate_projects_have_access_to_network(self, network, project_ids):
318318
if project_ids - allowed_projects:
319319
raise exc.InvalidSharedSetting(network=network.name)
320320

321-
def _validate_ipv6_attributes(self, subnet, cur_subnet):
321+
def _validate_ipv6_attributes(self, subnet, cur_subnet, has_cidr):
322322
if cur_subnet:
323323
self._validate_ipv6_update_dhcp(subnet, cur_subnet)
324324
return
@@ -330,7 +330,7 @@ def _validate_ipv6_attributes(self, subnet, cur_subnet):
330330
if ra_mode_set and address_mode_set:
331331
self._validate_ipv6_combination(subnet['ipv6_ra_mode'],
332332
subnet['ipv6_address_mode'])
333-
if address_mode_set or ra_mode_set:
333+
if has_cidr and (address_mode_set or ra_mode_set):
334334
self._validate_eui64_applicable(subnet)
335335

336336
def _validate_eui64_applicable(self, subnet):
@@ -607,16 +607,21 @@ def _validate_subnet(self, context, s, cur_subnet=None):
607607

608608
ip_ver = s['ip_version']
609609

610+
# We could be called without a cidr if a subnet pool is being used,
611+
# so remember that since some checks below require one and should
612+
# be skipped.
613+
has_cidr = False
610614
if validators.is_attr_set(s.get('cidr')):
611615
self._validate_ip_version(ip_ver, s['cidr'], 'cidr')
616+
has_cidr = True
612617

613618
# TODO(watanabe.isao): After we found a way to avoid the re-sync
614619
# from the agent side, this restriction could be removed.
615620
if cur_subnet:
616621
dhcp_was_enabled = cur_subnet.enable_dhcp
617622
else:
618623
dhcp_was_enabled = False
619-
if s.get('enable_dhcp') and not dhcp_was_enabled:
624+
if has_cidr and s.get('enable_dhcp') and not dhcp_was_enabled:
620625
subnet_prefixlen = netaddr.IPNetwork(s['cidr']).prefixlen
621626
error_message = _("Subnet has a prefix length that is "
622627
"incompatible with DHCP service enabled")
@@ -640,12 +645,13 @@ def _validate_subnet(self, context, s, cur_subnet=None):
640645

641646
if validators.is_attr_set(s.get('gateway_ip')):
642647
self._validate_ip_version(ip_ver, s['gateway_ip'], 'gateway_ip')
643-
is_gateway_not_valid = (
644-
ipam.utils.check_gateway_invalid_in_subnet(
645-
s['cidr'], s['gateway_ip']))
646-
if is_gateway_not_valid:
647-
error_message = _("Gateway is not valid on subnet")
648-
raise exc.InvalidInput(error_message=error_message)
648+
if has_cidr:
649+
is_gateway_not_valid = (
650+
ipam.utils.check_gateway_invalid_in_subnet(
651+
s['cidr'], s['gateway_ip']))
652+
if is_gateway_not_valid:
653+
error_message = _("Gateway is not valid on subnet")
654+
raise exc.InvalidInput(error_message=error_message)
649655
# Ensure the gateway IP is not assigned to any port
650656
# skip this check in case of create (s parameter won't have id)
651657
# NOTE(salv-orlando): There is slight chance of a race, when
@@ -698,7 +704,7 @@ def _validate_subnet(self, context, s, cur_subnet=None):
698704
error_message=(_("ipv6_address_mode is not valid when "
699705
"ip_version is 4")))
700706
if ip_ver == 6:
701-
self._validate_ipv6_attributes(s, cur_subnet)
707+
self._validate_ipv6_attributes(s, cur_subnet, has_cidr)
702708

703709
def _validate_subnet_for_pd(self, subnet):
704710
"""Validates that subnet parameters are correct for IPv6 PD"""
@@ -860,6 +866,7 @@ def _create_subnet_precommit(self, context, subnet):
860866
msg = _('a subnetpool must be specified in the absence of a cidr')
861867
raise exc.BadRequest(resource='subnets', msg=msg)
862868

869+
validate = True
863870
if subnetpool_id:
864871
self.ipam.validate_pools_with_subnetpool(s)
865872
if subnetpool_id == constants.IPV6_PD_POOL_ID:
@@ -868,16 +875,18 @@ def _create_subnet_precommit(self, context, subnet):
868875
# cidr with IPv6 prefix delegation. Set the subnetpool_id
869876
# to None and allow the request to continue as normal.
870877
subnetpool_id = None
871-
self._validate_subnet(context, s)
872878
else:
873879
prefix = constants.PROVISIONAL_IPV6_PD_PREFIX
874880
subnet['subnet']['cidr'] = prefix
875881
self._validate_subnet_for_pd(s)
882+
validate = False
876883
else:
877884
if not has_cidr:
878885
msg = _('A cidr must be specified in the absence of a '
879886
'subnet pool')
880887
raise exc.BadRequest(resource='subnets', msg=msg)
888+
889+
if validate:
881890
self._validate_subnet(context, s)
882891

883892
with db_api.CONTEXT_WRITER.using(context):

neutron/services/segments/db.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
from oslo_log import log as logging
3232
from oslo_utils import uuidutils
3333

34+
from neutron.db.models import agent as agent_model
35+
from neutron.db.models import segment as segment_model
3436
from neutron.db import segments_db as db
3537
from neutron import manager
3638
from neutron.objects import base as base_obj
@@ -243,14 +245,43 @@ def update_segment_host_mapping(context, host, current_segment_ids):
243245
entry.segment_id, entry.host)
244246

245247

246-
def get_hosts_mapped_with_segments(context):
248+
def get_hosts_mapped_with_segments(context, include_agent_types=None,
249+
exclude_agent_types=None):
247250
"""Get hosts that are mapped with segments.
248251
249252
L2 providers can use this method to get an overview of SegmentHostMapping,
250253
and then delete the stale SegmentHostMapping.
254+
255+
When using both include_agent_types and exclude_agent_types,
256+
exclude_agent_types is most significant.
257+
All hosts without agent are excluded when using any agent_type filter.
258+
259+
:param context: current running context information
260+
:param include_agent_types: (set) List of agent types, include hosts
261+
with matching agents.
262+
:param exclude_agent_types: (set) List of agent types, exclude hosts
263+
with matching agents.
251264
"""
252-
segment_host_mapping = network.SegmentHostMapping.get_objects(context)
253-
return {row.host for row in segment_host_mapping}
265+
def add_filter_by_agent_types(qry, include, exclude):
266+
qry = qry.join(
267+
agent_model.Agent,
268+
segment_model.SegmentHostMapping.host == agent_model.Agent.host)
269+
if include:
270+
qry = qry.filter(agent_model.Agent.agent_type.in_(include))
271+
if exclude:
272+
qry = qry.filter(agent_model.Agent.agent_type.not_in(exclude))
273+
274+
return qry
275+
276+
with db_api.CONTEXT_READER.using(context):
277+
query = context.session.query(segment_model.SegmentHostMapping)
278+
if include_agent_types or exclude_agent_types:
279+
query = add_filter_by_agent_types(query, include_agent_types,
280+
exclude_agent_types)
281+
282+
res = query.all()
283+
284+
return {row.host for row in res}
254285

255286

256287
def _get_phys_nets(agent):

neutron/tests/unit/db/test_db_base_plugin_v2.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6657,6 +6657,47 @@ def test_allocate_any_ipv4_subnet_ipv6_pool(self):
66576657
res = req.get_response(self.api)
66586658
self.assertEqual(400, res.status_int)
66596659

6660+
def _test_allocate_any_subnet_wrong_dns(self, pool_cidr, ip_version,
6661+
nameservers):
6662+
with self.network() as network:
6663+
sp = self._test_create_subnetpool([pool_cidr],
6664+
tenant_id=self._tenant_id,
6665+
name=self._POOL_NAME)
6666+
6667+
# Specify a DNS nameserver of the wrong IP version
6668+
data = {'subnet': {'network_id': network['network']['id'],
6669+
'subnetpool_id': sp['subnetpool']['id'],
6670+
'ip_version': ip_version,
6671+
'dns_nameservers': nameservers,
6672+
'tenant_id': network['network']['tenant_id']}}
6673+
req = self.new_create_request('subnets', data)
6674+
res = req.get_response(self.api)
6675+
self.assertEqual(400, res.status_int)
6676+
6677+
def test_allocate_any_v4_subnet_wrong_dns_v6(self):
6678+
self._test_allocate_any_subnet_wrong_dns('10.0.0.0/16',
6679+
constants.IP_VERSION_4, ['2001:db8:1:2::1'])
6680+
6681+
def test_allocate_any_v6_subnet_wrong_dns_v4(self):
6682+
self._test_allocate_any_subnet_wrong_dns('2001:db8:1:2::/63',
6683+
constants.IP_VERSION_6, ['10.0.0.1'])
6684+
6685+
def test_allocate_any_v4_subnet_wrong_dns_v6_multiple(self):
6686+
self._test_allocate_any_subnet_wrong_dns('10.0.0.0/16',
6687+
constants.IP_VERSION_4, ['2001:db8:1:2::1', '2001:db8:2:2::1'])
6688+
6689+
def test_allocate_any_v6_subnet_wrong_dns_v4_multiple(self):
6690+
self._test_allocate_any_subnet_wrong_dns('2001:db8:1:2::/63',
6691+
constants.IP_VERSION_6, ['11.0.0.11', '12.0.0.12'])
6692+
6693+
def test_allocate_any_v4_subnet_wrong_dns_mixed(self):
6694+
self._test_allocate_any_subnet_wrong_dns('10.0.0.0/16',
6695+
constants.IP_VERSION_4, ['11.0.0.11', '2001:db8:1:2::1'])
6696+
6697+
def test_allocate_any_v6_subnet_wrong_dns_mixed(self):
6698+
self._test_allocate_any_subnet_wrong_dns('2001:db8:1:2::/63',
6699+
constants.IP_VERSION_6, ['11.0.0.11', '2001:db8:1:2::1'])
6700+
66606701

66616702
class DbModelMixin(object):
66626703
"""DB model tests."""

neutron/tests/unit/extensions/test_segment.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,10 +748,37 @@ def test_get_all_hosts_mapped_with_segments(self):
748748
actual_hosts = db.get_hosts_mapped_with_segments(ctx)
749749
self.assertEqual(hosts, actual_hosts)
750750

751+
def test_get_all_hosts_mapped_with_segments_agent_type_filter(self):
752+
ctx = context.get_admin_context()
753+
hosts = set()
754+
with self.network() as network:
755+
network_id = network['network']['id']
756+
for i in range(1, 3):
757+
host = "host%s" % i
758+
segment = self._test_create_segment(
759+
network_id=network_id, physical_network='physnet%s' % i,
760+
segmentation_id=200 + i, network_type=constants.TYPE_VLAN)
761+
db.update_segment_host_mapping(
762+
ctx, host, {segment['segment']['id']})
763+
hosts.add(host)
764+
765+
# Now they are 2 hosts with segment being mapped.
766+
# host1 does not have an agent
767+
# host2 does not have an agent
768+
# Any agent_type filter excludes hosts that does not have an agent
769+
actual_hosts = db.get_hosts_mapped_with_segments(
770+
ctx, exclude_agent_types={'fake-agent-type'})
771+
self.assertEqual(set(), actual_hosts)
772+
actual_hosts = db.get_hosts_mapped_with_segments(
773+
ctx, include_agent_types={'fake-agent-type'})
774+
self.assertEqual(set(), actual_hosts)
775+
751776

752777
class TestMl2HostSegmentMappingOVS(HostSegmentMappingTestCase):
753778
_mechanism_drivers = ['openvswitch', 'logger']
754779
mock_path = 'neutron.services.segments.db.update_segment_host_mapping'
780+
agent_type_a = constants.AGENT_TYPE_OVS
781+
agent_type_b = constants.AGENT_TYPE_LINUXBRIDGE
755782

756783
def test_new_agent(self):
757784
host = 'host1'
@@ -871,9 +898,118 @@ def test_agent_with_no_mappings(self, mock):
871898
self.assertFalse(segments_host_db)
872899
self.assertFalse(mock.mock_calls)
873900

901+
def test_get_all_hosts_mapped_with_segments(self):
902+
ctx = context.get_admin_context()
903+
hosts = set()
904+
with self.network() as network:
905+
network_id = network['network']['id']
906+
for i in range(1, 3):
907+
host = "host%s" % i
908+
segment = self._test_create_segment(
909+
network_id=network_id, physical_network='physnet%s' % i,
910+
segmentation_id=200 + i, network_type=constants.TYPE_VLAN)
911+
self._register_agent(host, mappings={'physnet%s' % i: 'br-eth-1'},
912+
plugin=self.plugin)
913+
db.update_segment_host_mapping(
914+
ctx, host, {segment['segment']['id']})
915+
hosts.add(host)
916+
917+
# Now they are 2 hosts with segment being mapped.
918+
actual_hosts = db.get_hosts_mapped_with_segments(ctx)
919+
self.assertEqual(hosts, actual_hosts)
920+
921+
def test_get_all_hosts_mapped_with_segments_agent_type_filters(self):
922+
ctx = context.get_admin_context()
923+
with self.network() as network:
924+
network_id = network['network']['id']
925+
for i in range(1, 3):
926+
host = "host%s" % i
927+
segment = self._test_create_segment(
928+
network_id=network_id, physical_network='physnet%s' % i,
929+
segmentation_id=200 + i, network_type=constants.TYPE_VLAN)
930+
if i == 2:
931+
agent_type = self.agent_type_a
932+
else:
933+
agent_type = self.agent_type_b
934+
helpers.register_ovs_agent(
935+
host, agent_type=agent_type,
936+
bridge_mappings={'physnet%s' % i: 'br-eth-1'},
937+
plugin=self.plugin, start_flag=True)
938+
db.update_segment_host_mapping(
939+
ctx, host, {segment['segment']['id']})
940+
941+
# Now they are 2 hosts with segment being mapped.
942+
# host1 is agent_type_b
943+
# host2 is agent_type_a
944+
# get all hosts (host1 and host2) when not using any filtering
945+
actual_hosts = db.get_hosts_mapped_with_segments(ctx)
946+
self.assertEqual({"host1", "host2"}, actual_hosts)
947+
# get host1 when exclude agent_type_a agents
948+
actual_hosts = db.get_hosts_mapped_with_segments(
949+
ctx, exclude_agent_types={self.agent_type_a})
950+
self.assertEqual({"host1"}, actual_hosts)
951+
# get host2 when exclude agent_type_b agents
952+
actual_hosts = db.get_hosts_mapped_with_segments(
953+
ctx, exclude_agent_types={self.agent_type_b})
954+
self.assertEqual({"host2"}, actual_hosts)
955+
# get host2 when include agent_type_a agents
956+
actual_hosts = db.get_hosts_mapped_with_segments(
957+
ctx, include_agent_types={self.agent_type_a})
958+
self.assertEqual({"host2"}, actual_hosts)
959+
# get host1 when include agent_type_b agents
960+
actual_hosts = db.get_hosts_mapped_with_segments(
961+
ctx, include_agent_types={self.agent_type_b})
962+
self.assertEqual({"host1"}, actual_hosts)
963+
# get host1 and host2 when include both agent_type_a and agent_type_b
964+
actual_hosts = db.get_hosts_mapped_with_segments(
965+
ctx, include_agent_types={self.agent_type_b, self.agent_type_a})
966+
self.assertEqual({"host1", "host2"}, actual_hosts)
967+
# When using both include and exclude, exclude is most significant
968+
actual_hosts = db.get_hosts_mapped_with_segments(
969+
ctx,
970+
include_agent_types={self.agent_type_b, self.agent_type_a},
971+
exclude_agent_types={self.agent_type_b}
972+
)
973+
self.assertEqual({"host2"}, actual_hosts)
974+
# include and exclude both agent types - exclude is most significant
975+
actual_hosts = db.get_hosts_mapped_with_segments(
976+
ctx,
977+
include_agent_types={self.agent_type_b, self.agent_type_a},
978+
exclude_agent_types={self.agent_type_b, self.agent_type_a}
979+
)
980+
self.assertEqual(set(), actual_hosts)
981+
982+
def test_get_all_hosts_mapped_with_segments_agent_type_filter(self):
983+
ctx = context.get_admin_context()
984+
hosts = set()
985+
with self.network() as network:
986+
network_id = network['network']['id']
987+
for i in range(1, 3):
988+
host = "host%s" % i
989+
segment = self._test_create_segment(
990+
network_id=network_id, physical_network='physnet%s' % i,
991+
segmentation_id=200 + i, network_type=constants.TYPE_VLAN)
992+
self._register_agent(host, mappings={'physnet%s' % i: 'br-eth-1'},
993+
plugin=self.plugin)
994+
db.update_segment_host_mapping(
995+
ctx, host, {segment['segment']['id']})
996+
hosts.add(host)
997+
998+
# Now they are 2 hosts with segment being mapped.
999+
# host1 is agent_type_a
1000+
# host2 is agent_type_a
1001+
actual_hosts = db.get_hosts_mapped_with_segments(
1002+
ctx, exclude_agent_types={self.agent_type_a})
1003+
self.assertEqual(set(), actual_hosts)
1004+
actual_hosts = db.get_hosts_mapped_with_segments(
1005+
ctx, include_agent_types={self.agent_type_a})
1006+
self.assertEqual(hosts, actual_hosts)
1007+
8741008

8751009
class TestMl2HostSegmentMappingLinuxBridge(TestMl2HostSegmentMappingOVS):
8761010
_mechanism_drivers = ['linuxbridge', 'logger']
1011+
agent_type_a = constants.AGENT_TYPE_LINUXBRIDGE
1012+
agent_type_b = constants.AGENT_TYPE_OVS
8771013

8781014
def setUp(self, plugin=None):
8791015
cfg.CONF.set_override(c_experimental.EXPERIMENTAL_LINUXBRIDGE, True,
@@ -888,6 +1024,8 @@ def _register_agent(self, host, mappings=None, plugin=None):
8881024

8891025
class TestMl2HostSegmentMappingMacvtap(TestMl2HostSegmentMappingOVS):
8901026
_mechanism_drivers = ['macvtap', 'logger']
1027+
agent_type_a = constants.AGENT_TYPE_MACVTAP
1028+
agent_type_b = constants.AGENT_TYPE_OVS
8911029

8921030
def _register_agent(self, host, mappings=None, plugin=None):
8931031
helpers.register_macvtap_agent(host=host, interface_mappings=mappings,
@@ -896,6 +1034,8 @@ def _register_agent(self, host, mappings=None, plugin=None):
8961034

8971035
class TestMl2HostSegmentMappingSriovNicSwitch(TestMl2HostSegmentMappingOVS):
8981036
_mechanism_drivers = ['sriovnicswitch', 'logger']
1037+
agent_type_a = constants.AGENT_TYPE_NIC_SWITCH
1038+
agent_type_b = constants.AGENT_TYPE_OVS
8991039

9001040
def _register_agent(self, host, mappings=None, plugin=None):
9011041
helpers.register_sriovnicswitch_agent(host=host,

0 commit comments

Comments
 (0)