[OVN][Placement] Placement init config with Chassis create event
Now the initial Placement configuration per Chassis is build when a Chassis create event is received. That will happen when: * The Neutron API is restarted. * When a new Chassis register is created. Closes-Bug: #2102090 Change-Id: I9aa8accdcead079fe2e713fe569f8bff7403be92
This commit is contained in:
parent
89053ab55f
commit
5841cacecb
@ -325,13 +325,11 @@ by commas.
|
||||
This information is retrieved from the OVN SB database during the Neutron
|
||||
server initialization and when the "Chassis" registers are updated.
|
||||
|
||||
During the Neutron server initialization, a ``MaintenanceWorker`` thread will
|
||||
call ``OvnSbSynchronizer.do_sync``, that will call
|
||||
``OVNClientPlacementExtension.read_initial_chassis_config``. This method lists
|
||||
all chassis and builds the resource provider information needed by Placement.
|
||||
This information is stored in the "Chassis" registers, in
|
||||
"external_ids:ovn-cms-options", with the same format as retrieved from the
|
||||
local "Open_vSwitch" registers from each chassis.
|
||||
The initial Placement configuration is retrieved when the Neutron API receives
|
||||
a "Chassis" create event, that happens when the IDL is connected to the
|
||||
database server. When a creation event is received, the Neutron API reads the
|
||||
configuration, builds a ``PlacementState`` instance and sends it to the
|
||||
Placement API.
|
||||
|
||||
The second method to update the Placement information is when a "Chassis"
|
||||
registers is updated. The ``OVNClientPlacementExtension`` extension registers
|
||||
|
@ -125,16 +125,19 @@ class ChassisBandwidthConfigEvent(row_event.RowEvent):
|
||||
if self._driver._post_fork_event.is_set():
|
||||
return self._driver._ovn_client.placement_extension
|
||||
|
||||
@property
|
||||
def placement_extension_enabled(self):
|
||||
return self.placement_extension and self.placement_extension.enabled
|
||||
|
||||
def match_fn(self, event, row, old=None):
|
||||
# If the OVNMechanismDriver OVNClient has not been instantiated, the
|
||||
# event is skipped. All chassis configurations are read during the
|
||||
# OVN placement extension initialization.
|
||||
if (not self.placement_extension or
|
||||
not self.placement_extension.enabled):
|
||||
return False
|
||||
if event == self.ROW_CREATE:
|
||||
return True
|
||||
if event == self.ROW_UPDATE and old and hasattr(old, 'other_config'):
|
||||
|
||||
# If the OVNMechanismDriver OVNClient has not been instantiated, the
|
||||
# update event is skipped.
|
||||
if not self.placement_extension_enabled:
|
||||
return False
|
||||
if old and hasattr(old, 'other_config'):
|
||||
row_bw = _parse_ovn_cms_options(row)
|
||||
old_bw = _parse_ovn_cms_options(old)
|
||||
if row_bw != old_bw:
|
||||
@ -142,6 +145,14 @@ class ChassisBandwidthConfigEvent(row_event.RowEvent):
|
||||
return False
|
||||
|
||||
def run(self, event, row, old):
|
||||
if event == self.ROW_CREATE:
|
||||
# It is possible that a Chassis create event is received before
|
||||
# the OVNMechanismDriver OVNClient has been instantiated. Wait for
|
||||
# it and check the Placement extension.
|
||||
self._driver._post_fork_event.wait()
|
||||
if not self.placement_extension_enabled:
|
||||
return
|
||||
|
||||
name2uuid = self.placement_extension.name2uuid()
|
||||
state = self.placement_extension.build_placement_state(row, name2uuid)
|
||||
if not state:
|
||||
@ -149,8 +160,8 @@ class ChassisBandwidthConfigEvent(row_event.RowEvent):
|
||||
|
||||
_send_deferred_batch(state)
|
||||
ch_config = dict_chassis_config(state)
|
||||
LOG.debug('OVN chassis %(chassis)s Placement configuration modified: '
|
||||
'%(config)s', {'chassis': row.name, 'config': ch_config})
|
||||
LOG.info('OVN chassis %(chassis)s Placement configuration modified: '
|
||||
'%(config)s', {'chassis': row.name, 'config': ch_config})
|
||||
|
||||
|
||||
@common_utils.SingletonDecorator
|
||||
@ -228,7 +239,7 @@ class OVNClientPlacementExtension:
|
||||
msg = ', '.join(['Chassis {}: {}'.format(
|
||||
name, dict_chassis_config(state))
|
||||
for (name, state) in chassis.items()]) or '(no info)'
|
||||
LOG.debug('OVN chassis Placement initial configuration: %s', msg)
|
||||
LOG.info('OVN chassis Placement initial configuration: %s', msg)
|
||||
return chassis
|
||||
|
||||
def name2uuid(self, name=None):
|
||||
|
@ -1351,9 +1351,6 @@ class OvnSbSynchronizer(OvnDbSynchronizer):
|
||||
self.sync_hostname_and_physical_networks(ctx)
|
||||
if utils.is_ovn_l3(self.l3_plugin):
|
||||
self.l3_plugin.schedule_unhosted_gateways()
|
||||
# NOTE(ralonsoh): this could be called using a resource event.
|
||||
self.ovn_driver._ovn_client.placement_extension.\
|
||||
read_initial_chassis_config()
|
||||
|
||||
LOG.debug("OVN-Southbound DB sync process completed @ %s",
|
||||
str(datetime.now()))
|
||||
|
@ -223,3 +223,34 @@ class TestOVNClientPlacementExtension(base.TestOVNFunctionalBase):
|
||||
common_utils.wait_until_true,
|
||||
lambda: mock_send_placement.called,
|
||||
timeout=2)
|
||||
|
||||
@mock.patch.object(placement_extension, '_send_deferred_batch')
|
||||
def test_chassis_bandwidth_initial_config_event(self, mock_send_placement):
|
||||
ch_name = uuidutils.generate_uuid()
|
||||
rp_uuid = uuidutils.generate_uuid()
|
||||
ch_event = test_ovsdb_monitor.WaitForChassisPrivateCreateEvent(ch_name)
|
||||
self.mech_driver.sb_ovn.idl.notify_handler.watch_event(ch_event)
|
||||
self.mock_name2uuid.return_value = {'host1': rp_uuid}
|
||||
self._create_chassis(
|
||||
'host1', ch_name, physical_nets=['phys1'],
|
||||
bandwidths='br-provider0:1000:2000',
|
||||
inventory_defaults='allocation_ratio:1.0;min_unit:2',
|
||||
hypervisors='br-provider0:host1')
|
||||
self.assertTrue(ch_event.wait())
|
||||
common_utils.wait_until_true(lambda: mock_send_placement.called,
|
||||
timeout=2)
|
||||
mock_send_placement.assert_called_once()
|
||||
placement_state = mock_send_placement.call_args[0][0]
|
||||
|
||||
device_mappings = {'phys1': ['br-provider0']}
|
||||
self.assertEqual(placement_state._device_mappings, device_mappings)
|
||||
|
||||
hypervisor_rps = {'br-provider0': {'name': 'host1', 'uuid': rp_uuid}}
|
||||
self.assertEqual(placement_state._hypervisor_rps, hypervisor_rps)
|
||||
|
||||
rp_bandwidths = {'br-provider0': {'egress': 1000, 'ingress': 2000}}
|
||||
self.assertEqual(placement_state._rp_bandwidths, rp_bandwidths)
|
||||
|
||||
rp_inventory_defaults = {'allocation_ratio': 1.0, 'min_unit': 2}
|
||||
self.assertEqual(placement_state._rp_inventory_defaults,
|
||||
rp_inventory_defaults)
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
issues:
|
||||
- |
|
||||
The ML2/OVN Placement initial configuration is executed now in the Neutron
|
||||
API process and removed from the maintenance worker; since the migration
|
||||
to WSGI, now the API and the maintenance worker are different processes.
|
||||
When an OVN ``Chassis`` creation event is received, the configuration is
|
||||
read, a ``PlacementState`` object created and sent to the Placement API.
|
Loading…
x
Reference in New Issue
Block a user