Update Nova aggregates on changed host mappings

When creating a subnet on a segment, Nova aggregates are updated
with the host information. But when adding a compute node to an
existing segment or modifying what segments a node is attached to,
Nova was not updated with these changes for ML2/OVN.

ML2/OVS has agent code which via report_state() will call
create_or_update_agent() which causes the aggregates to eventually
get updated via AGENT_AFTER_CREATE events, etc.

ML2/OVN does not have "real" agents. It monkeypatches some agent
methods to respond to the API requests itself--but it does not use
the agents db--which is what create_or_update_agent() modifies.

But it shouldn't be necessary to rely on updates from the agent in
our case, as the segments code can see segment host mappings being updated and just directly handle notifying nova when those change.

Closes-Bug: #2096941
Change-Id: I8112076f8acb821752941396e7aa39ecb1352ca3
This commit is contained in:
Terry Wilson
2024-11-22 00:00:42 +00:00
parent b1ebfd62d5
commit 2d8fe38ad5
2 changed files with 52 additions and 10 deletions

View File

@@ -235,6 +235,17 @@ def update_segment_host_mapping(context, host, current_segment_ids):
for segment_id in segment_ids:
network.SegmentHostMapping(
context, segment_id=segment_id, host=host).create()
if segment_ids:
registry.publish(
resources.SEGMENT_HOST_MAPPING,
events.AFTER_CREATE,
update_segment_host_mapping,
payload=events.DBEventPayload(
context,
metadata={
'host': host,
'current_segment_ids': segment_ids}))
LOG.debug('Segments %s mapped to the host %s', segment_ids, host)
stale_segment_ids = previous_segment_ids - current_segment_ids
if stale_segment_ids:
@@ -243,6 +254,15 @@ def update_segment_host_mapping(context, host, current_segment_ids):
entry.delete()
LOG.debug('Segment %s unmapped from host %s',
entry.segment_id, entry.host)
registry.publish(
resources.SEGMENT_HOST_MAPPING,
events.AFTER_DELETE,
update_segment_host_mapping,
payload=events.DBEventPayload(
context,
metadata={
'host': host,
'deleted_segment_ids': stale_segment_ids}))
def get_hosts_mapped_with_segments(context, include_agent_types=None,
@@ -352,12 +372,6 @@ def _update_segment_host_mapping_for_agent(resource, event, trigger,
segment['id'] for segment in segments
if check_segment_for_agent(segment, agent)}
update_segment_host_mapping(context, host, current_segment_ids)
registry.publish(resources.SEGMENT_HOST_MAPPING, events.AFTER_CREATE,
plugin, payload=events.DBEventPayload(
context,
metadata={
'host': host,
'current_segment_ids': current_segment_ids}))
def _add_segment_host_mapping_for_segment(resource, event, trigger,

View File

@@ -393,13 +393,18 @@ class NovaSegmentNotifier:
LOG.info('Segment %s resource provider not found; error: %s',
event.segment_id, str(exc))
@staticmethod
def _payload_segment_ids(payload, key):
# NOTE(twilson) My assumption is that this is to guarantee the subnets
# passed exist in at least one subnet
subnets = subnet_obj.Subnet.get_objects(
payload.context, segment_id=payload.metadata.get(key))
return {s.segment_id for s in subnets}
@registry.receives(resources.SEGMENT_HOST_MAPPING, [events.AFTER_CREATE])
def _notify_host_addition_to_aggregate(self, resource, event, trigger,
payload=None):
subnets = subnet_obj.Subnet.get_objects(
payload.context,
segment_id=payload.metadata.get('current_segment_ids'))
segment_ids = {s.segment_id for s in subnets}
segment_ids = self._payload_segment_ids(payload, 'current_segment_ids')
self.batch_notifier.queue_event(
Event(self._add_host_to_aggregate,
segment_ids, host=payload.metadata.get('host')))
@@ -420,6 +425,29 @@ class NovaSegmentNotifier:
'routed network segment %(segment_id)s',
{'host': event.host, 'segment_id': segment_id})
@registry.receives(resources.SEGMENT_HOST_MAPPING, [events.AFTER_DELETE])
def _notify_host_removal_from_aggregate(self, resource, event, trigger,
payload=None):
segment_ids = self._payload_segment_ids(payload, 'deleted_segment_ids')
self.batch_notifier.queue_event(
Event(self._remove_host_from_aggregate,
segment_ids, host=payload.metadata.get('host')))
def _remove_host_from_aggregate(self, event):
for segment_id in event.segment_ids:
aggregate_id = self._get_aggregate_id(segment_id)
if not aggregate_id:
LOG.info('When removing host %(host)s, aggregate not found '
'for routed network segment %(segment_id)s',
{'host': event.host, 'segment_id': segment_id})
continue
try:
self.n_client.aggregates.remove_host(aggregate_id, event.host)
except nova_exc.NotFound:
LOG.info('Host %(host)s is not in aggregate for '
'routed network segment %(segment_ids)s',
{'host': event.host, 'segment_id': segment_id})
@registry.receives(resources.PORT, [events.AFTER_CREATE,
events.AFTER_DELETE])
def _notify_port_created_or_deleted(self, resource, event, trigger,