[OVN] Set the Neutron port status based on "lsp.up" and "lsp.enabled"
The Neutron "port.status" field ("ACTIVE", "DOWN") is set depending on the Logical Switch Port "up" and "enabled" flags. Before this patch, the "port.status" depended only on the "up" flag. However, the user can set the "port.admin_state_up" field that will modify the "lsp.enabled" flag. If the "port.admin_state_up" is DOWN, the port does not transmit and the status should be "DOWN". The OVN backend is correctly disabling the port transmission; what was incorrect in the Neutron API is only the "port.status" field. This is currently working in other mechanism drivers like LB or OVS. Closes-Bug: #2036705 Change-Id: I5b7b55b0b365df7246a571cea97a392cdf89bdb6
This commit is contained in:
parent
1879d92533
commit
aead4aa99d
@ -301,6 +301,33 @@ def get_lsp_security_groups(port, skip_trusted_port=True):
|
|||||||
) else port.get('security_groups', [])
|
) else port.get('security_groups', [])
|
||||||
|
|
||||||
|
|
||||||
|
def is_lsp_enabled(lsp):
|
||||||
|
"""Return if a Logical Switch Port is enabled
|
||||||
|
|
||||||
|
This method mimics the OVN northd method ``lsp_is_enabled``
|
||||||
|
https://shorturl.at/rBSZ7. The "enabled" field can have three values:
|
||||||
|
* []: from older OVN versions, in this case the LSP is enabled by default.
|
||||||
|
* [True]: the LSP is enabled.
|
||||||
|
* [False]: the LSP is disabled.
|
||||||
|
|
||||||
|
:param lsp: ``ovs.db.Row`` with a Logical Switch Port register.
|
||||||
|
:return: True if the port is enabled, False if not.
|
||||||
|
"""
|
||||||
|
return not lsp.enabled or lsp.enabled == [True]
|
||||||
|
|
||||||
|
|
||||||
|
def is_lsp_up(lsp):
|
||||||
|
"""Return if a Logical Switch Port is UP
|
||||||
|
|
||||||
|
This method mimics the OVN northd method ``lsp_is_up``
|
||||||
|
https://shorturl.at/aoKR6
|
||||||
|
|
||||||
|
:param lsp: ``ovs.db.Row`` with a Logical Switch Port register.
|
||||||
|
:return: True if the port is UP, False if not.
|
||||||
|
"""
|
||||||
|
return lsp.up == [True]
|
||||||
|
|
||||||
|
|
||||||
def is_snat_enabled(router):
|
def is_snat_enabled(router):
|
||||||
return router.get(l3.EXTERNAL_GW_INFO, {}).get('enable_snat', True)
|
return router.get(l3.EXTERNAL_GW_INFO, {}).get('enable_snat', True)
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ class PortBindingChassisUpdateEvent(row_event.RowEvent):
|
|||||||
# ``LogicalSwitchPortUpdateUpEvent`` events.
|
# ``LogicalSwitchPortUpdateUpEvent`` events.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return bool(lsp.up)
|
return utils.is_lsp_enabled(lsp) and utils.is_lsp_up(lsp)
|
||||||
|
|
||||||
def run(self, event, row, old=None):
|
def run(self, event, row, old=None):
|
||||||
self.driver.set_port_status_up(row.logical_port)
|
self.driver.set_port_status_up(row.logical_port)
|
||||||
@ -461,81 +461,84 @@ class PortBindingChassisEvent(row_event.RowEvent):
|
|||||||
router, host)
|
router, host)
|
||||||
|
|
||||||
|
|
||||||
class LogicalSwitchPortCreateUpEvent(row_event.RowEvent):
|
class LogicalSwitchPortCreateEvent(row_event.RowEvent):
|
||||||
"""Row create event - Logical_Switch_Port 'up' = True.
|
"""Row create event - Checks Logical_Switch_Port is UP and enabled.
|
||||||
|
|
||||||
On connection, we get a dump of all ports, so if there is a neutron
|
On connection, we get a dump of all ports, so if there is a neutron
|
||||||
port that is down that has since been activated, we'll catch it here.
|
port that has been activated or deactivated, we'll catch it here.
|
||||||
This event will not be generated for new ports getting created.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, driver):
|
def __init__(self, driver):
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
table = 'Logical_Switch_Port'
|
table = 'Logical_Switch_Port'
|
||||||
events = (self.ROW_CREATE,)
|
events = (self.ROW_CREATE,)
|
||||||
super(LogicalSwitchPortCreateUpEvent, self).__init__(
|
super().__init__(events, table, [])
|
||||||
events, table, (('up', '=', True),))
|
self.event_name = 'LogicalSwitchPortCreateEvent'
|
||||||
self.event_name = 'LogicalSwitchPortCreateUpEvent'
|
|
||||||
|
|
||||||
def run(self, event, row, old):
|
def run(self, event, row, old):
|
||||||
self.driver.set_port_status_up(row.name)
|
if utils.is_lsp_up(row) and utils.is_lsp_enabled(row):
|
||||||
|
self.driver.set_port_status_up(row.name)
|
||||||
|
else:
|
||||||
class LogicalSwitchPortCreateDownEvent(row_event.RowEvent):
|
self.driver.set_port_status_down(row.name)
|
||||||
"""Row create event - Logical_Switch_Port 'up' = False
|
|
||||||
|
|
||||||
On connection, we get a dump of all ports, so if there is a neutron
|
|
||||||
port that is up that has since been deactivated, we'll catch it here.
|
|
||||||
This event will not be generated for new ports getting created.
|
|
||||||
"""
|
|
||||||
def __init__(self, driver):
|
|
||||||
self.driver = driver
|
|
||||||
table = 'Logical_Switch_Port'
|
|
||||||
events = (self.ROW_CREATE,)
|
|
||||||
super(LogicalSwitchPortCreateDownEvent, self).__init__(
|
|
||||||
events, table, (('up', '=', False),))
|
|
||||||
self.event_name = 'LogicalSwitchPortCreateDownEvent'
|
|
||||||
|
|
||||||
def run(self, event, row, old):
|
|
||||||
self.driver.set_port_status_down(row.name)
|
|
||||||
|
|
||||||
|
|
||||||
class LogicalSwitchPortUpdateUpEvent(row_event.RowEvent):
|
class LogicalSwitchPortUpdateUpEvent(row_event.RowEvent):
|
||||||
"""Row update event - Logical_Switch_Port 'up' going from False to True
|
"""Row update event - Logical_Switch_Port UP or enabled going True
|
||||||
|
|
||||||
This happens when the VM goes up.
|
This happens when the VM goes up.
|
||||||
New value of Logical_Switch_Port 'up' will be True and the old value will
|
|
||||||
be False.
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, driver):
|
def __init__(self, driver):
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
table = 'Logical_Switch_Port'
|
table = 'Logical_Switch_Port'
|
||||||
events = (self.ROW_UPDATE,)
|
events = (self.ROW_UPDATE,)
|
||||||
super(LogicalSwitchPortUpdateUpEvent, self).__init__(
|
super(LogicalSwitchPortUpdateUpEvent, self).__init__(
|
||||||
events, table, (('up', '=', True),),
|
events, table, None)
|
||||||
old_conditions=(('up', '!=', True),))
|
|
||||||
self.event_name = 'LogicalSwitchPortUpdateUpEvent'
|
self.event_name = 'LogicalSwitchPortUpdateUpEvent'
|
||||||
|
|
||||||
|
def match_fn(self, event, row, old):
|
||||||
|
if not (utils.is_lsp_up(row) and utils.is_lsp_enabled(row)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if hasattr(old, 'up') and not utils.is_lsp_up(old):
|
||||||
|
# The port has transitioned from DOWN to UP, and the admin state
|
||||||
|
# is UP (lsp.enabled=True)
|
||||||
|
return True
|
||||||
|
if hasattr(old, 'enabled') and not utils.is_lsp_enabled(old):
|
||||||
|
# The user has set the admin state to UP and the port is UP too.
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def run(self, event, row, old):
|
def run(self, event, row, old):
|
||||||
self.driver.set_port_status_up(row.name)
|
self.driver.set_port_status_up(row.name)
|
||||||
|
|
||||||
|
|
||||||
class LogicalSwitchPortUpdateDownEvent(row_event.RowEvent):
|
class LogicalSwitchPortUpdateDownEvent(row_event.RowEvent):
|
||||||
"""Row update event - Logical_Switch_Port 'up' going from True to False
|
"""Row update event - Logical_Switch_Port UP or enabled going to False
|
||||||
|
|
||||||
This happens when the VM goes down.
|
This happens when the VM goes down or the port is disabled.
|
||||||
New value of Logical_Switch_Port 'up' will be False and the old value will
|
|
||||||
be True.
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, driver):
|
def __init__(self, driver):
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
table = 'Logical_Switch_Port'
|
table = 'Logical_Switch_Port'
|
||||||
events = (self.ROW_UPDATE,)
|
events = (self.ROW_UPDATE,)
|
||||||
super(LogicalSwitchPortUpdateDownEvent, self).__init__(
|
super(LogicalSwitchPortUpdateDownEvent, self).__init__(
|
||||||
events, table, (('up', '=', False),),
|
events, table, None)
|
||||||
old_conditions=(('up', '=', True),))
|
|
||||||
self.event_name = 'LogicalSwitchPortUpdateDownEvent'
|
self.event_name = 'LogicalSwitchPortUpdateDownEvent'
|
||||||
|
|
||||||
|
def match_fn(self, event, row, old):
|
||||||
|
if (hasattr(old, 'up') and
|
||||||
|
utils.is_lsp_up(old) and
|
||||||
|
not utils.is_lsp_up(row)):
|
||||||
|
# If the port goes DOWN, update the port status to DOWN.
|
||||||
|
return True
|
||||||
|
if (hasattr(old, 'enabled') and
|
||||||
|
utils.is_lsp_enabled(old) and
|
||||||
|
not utils.is_lsp_enabled(row)):
|
||||||
|
# If the port is disabled by the user, update the port status to
|
||||||
|
# DOWN.
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def run(self, event, row, old):
|
def run(self, event, row, old):
|
||||||
self.driver.set_port_status_down(row.name)
|
self.driver.set_port_status_down(row.name)
|
||||||
|
|
||||||
@ -779,12 +782,10 @@ class OvnNbIdl(OvnIdlDistributedLock):
|
|||||||
super(OvnNbIdl, self).__init__(driver, remote, schema)
|
super(OvnNbIdl, self).__init__(driver, remote, schema)
|
||||||
self._lsp_update_up_event = LogicalSwitchPortUpdateUpEvent(driver)
|
self._lsp_update_up_event = LogicalSwitchPortUpdateUpEvent(driver)
|
||||||
self._lsp_update_down_event = LogicalSwitchPortUpdateDownEvent(driver)
|
self._lsp_update_down_event = LogicalSwitchPortUpdateDownEvent(driver)
|
||||||
self._lsp_create_up_event = LogicalSwitchPortCreateUpEvent(driver)
|
self._lsp_create_event = LogicalSwitchPortCreateEvent(driver)
|
||||||
self._lsp_create_down_event = LogicalSwitchPortCreateDownEvent(driver)
|
|
||||||
self._fip_create_delete_event = FIPAddDeleteEvent(driver)
|
self._fip_create_delete_event = FIPAddDeleteEvent(driver)
|
||||||
|
|
||||||
self.notify_handler.watch_events([self._lsp_create_up_event,
|
self.notify_handler.watch_events([self._lsp_create_event,
|
||||||
self._lsp_create_down_event,
|
|
||||||
self._lsp_update_up_event,
|
self._lsp_update_up_event,
|
||||||
self._lsp_update_down_event,
|
self._lsp_update_down_event,
|
||||||
self._fip_create_delete_event])
|
self._fip_create_delete_event])
|
||||||
@ -804,10 +805,8 @@ class OvnNbIdl(OvnIdlDistributedLock):
|
|||||||
After the startup, there is no need to watch these events.
|
After the startup, there is no need to watch these events.
|
||||||
So unwatch these events.
|
So unwatch these events.
|
||||||
"""
|
"""
|
||||||
self.notify_handler.unwatch_events([self._lsp_create_up_event,
|
self.notify_handler.unwatch_events([self._lsp_create_event])
|
||||||
self._lsp_create_down_event])
|
del self._lsp_create_event
|
||||||
self._lsp_create_up_event = None
|
|
||||||
self._lsp_create_down_event = None
|
|
||||||
|
|
||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
self.unwatch_logical_switch_port_create_events()
|
self.unwatch_logical_switch_port_create_events()
|
||||||
|
@ -60,7 +60,9 @@ OVN_NB_SCHEMA = {
|
|||||||
"port_security": {"type": {"key": "string",
|
"port_security": {"type": {"key": "string",
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": "unlimited"}},
|
"max": "unlimited"}},
|
||||||
"up": {"type": {"key": "boolean", "min": 0, "max": 1}}},
|
"up": {"type": {"key": "boolean", "min": 0, "max": 1}},
|
||||||
|
"enabled": {"type": {"key": "boolean", "min": 0, "max": 1}},
|
||||||
|
},
|
||||||
"indexes": [["name"]],
|
"indexes": [["name"]],
|
||||||
"isRoot": False,
|
"isRoot": False,
|
||||||
},
|
},
|
||||||
@ -366,7 +368,8 @@ class TestPortBindingChassisUpdateEvent(base.BaseTestCase):
|
|||||||
pbtable = fakes.FakeOvsdbTable.create_one_ovsdb_table(
|
pbtable = fakes.FakeOvsdbTable.create_one_ovsdb_table(
|
||||||
attrs={'name': 'Port_Binding'})
|
attrs={'name': 'Port_Binding'})
|
||||||
ovsdb_row = fakes.FakeOvsdbRow.create_one_ovsdb_row
|
ovsdb_row = fakes.FakeOvsdbRow.create_one_ovsdb_row
|
||||||
self.driver.nb_ovn.lookup.return_value = ovsdb_row(attrs={'up': True})
|
self.driver.nb_ovn.lookup.return_value = ovsdb_row(
|
||||||
|
attrs={'up': True, 'enabled': True})
|
||||||
|
|
||||||
# Port binding change.
|
# Port binding change.
|
||||||
self._test_event(
|
self._test_event(
|
||||||
@ -422,28 +425,46 @@ class TestOvnNbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase):
|
|||||||
# Execute the notifications queued
|
# Execute the notifications queued
|
||||||
self.idl.notify_handler.notify_loop()
|
self.idl.notify_handler.notify_loop()
|
||||||
|
|
||||||
def test_lsp_up_create_event(self):
|
def test_lsp_create_event(self):
|
||||||
row_data = {"up": True, "name": "foo-name"}
|
row_data = {'name': 'foo'}
|
||||||
|
|
||||||
|
# up and enabled
|
||||||
|
row_data.update({'up': True, 'enabled': True})
|
||||||
self._test_lsp_helper('create', row_data)
|
self._test_lsp_helper('create', row_data)
|
||||||
self.mech_driver.set_port_status_up.assert_called_once_with("foo-name")
|
self.mech_driver.set_port_status_up.assert_called_once_with('foo')
|
||||||
self.assertFalse(self.mech_driver.set_port_status_down.called)
|
self.assertFalse(self.mech_driver.set_port_status_down.called)
|
||||||
|
self.mech_driver.set_port_status_up.reset_mock()
|
||||||
|
|
||||||
def test_lsp_down_create_event(self):
|
# up and disabled
|
||||||
row_data = {"up": False, "name": "foo-name"}
|
row_data.update({'up': True, 'enabled': False})
|
||||||
self._test_lsp_helper('create', row_data)
|
|
||||||
self.mech_driver.set_port_status_down.assert_called_once_with(
|
|
||||||
"foo-name")
|
|
||||||
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
|
||||||
|
|
||||||
def test_lsp_up_not_set_event(self):
|
|
||||||
row_data = {"up": ['set', []], "name": "foo-name"}
|
|
||||||
self._test_lsp_helper('create', row_data)
|
self._test_lsp_helper('create', row_data)
|
||||||
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
||||||
self.assertFalse(self.mech_driver.set_port_status_down.called)
|
self.mech_driver.set_port_status_down.assert_called_once_with('foo')
|
||||||
|
self.mech_driver.set_port_status_down.reset_mock()
|
||||||
|
|
||||||
|
# down and enabled
|
||||||
|
row_data.update({'up': False, 'enabled': True})
|
||||||
|
self._test_lsp_helper('create', row_data)
|
||||||
|
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
||||||
|
self.mech_driver.set_port_status_down.assert_called_once_with('foo')
|
||||||
|
self.mech_driver.set_port_status_down.reset_mock()
|
||||||
|
|
||||||
|
# down and disabled
|
||||||
|
row_data.update({'up': False, 'enabled': False})
|
||||||
|
self._test_lsp_helper('create', row_data)
|
||||||
|
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
||||||
|
self.mech_driver.set_port_status_down.assert_called_once_with('foo')
|
||||||
|
self.mech_driver.set_port_status_down.reset_mock()
|
||||||
|
|
||||||
|
# Not set to up
|
||||||
|
row_data.update({'up': ['set', []], 'enabled': True})
|
||||||
|
self._test_lsp_helper('create', row_data)
|
||||||
|
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
||||||
|
self.mech_driver.set_port_status_down.assert_called_once_with('foo')
|
||||||
|
|
||||||
def test_unwatch_logical_switch_port_create_events(self):
|
def test_unwatch_logical_switch_port_create_events(self):
|
||||||
self.idl.unwatch_logical_switch_port_create_events()
|
self.idl.unwatch_logical_switch_port_create_events()
|
||||||
row_data = {"up": True, "name": "foo-name"}
|
row_data = {'up': False, 'name': 'foo-name'}
|
||||||
self._test_lsp_helper('create', row_data)
|
self._test_lsp_helper('create', row_data)
|
||||||
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
||||||
self.assertFalse(self.mech_driver.set_port_status_down.called)
|
self.assertFalse(self.mech_driver.set_port_status_down.called)
|
||||||
@ -455,11 +476,10 @@ class TestOvnNbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase):
|
|||||||
|
|
||||||
def test_post_connect(self):
|
def test_post_connect(self):
|
||||||
self.idl.post_connect()
|
self.idl.post_connect()
|
||||||
self.assertIsNone(self.idl._lsp_create_up_event)
|
self.assertIsNone(getattr(self.idl, '_lsp_create_event', None))
|
||||||
self.assertIsNone(self.idl._lsp_create_down_event)
|
|
||||||
|
|
||||||
def test_lsp_up_update_event(self):
|
def test_lsp_up_update_event(self):
|
||||||
new_row_json = {"up": True, "name": "foo-name"}
|
new_row_json = {'up': True, 'enabled': True, 'name': 'foo-name'}
|
||||||
old_row_json = {"up": False}
|
old_row_json = {"up": False}
|
||||||
self._test_lsp_helper('update', new_row_json,
|
self._test_lsp_helper('update', new_row_json,
|
||||||
old_row_json=old_row_json)
|
old_row_json=old_row_json)
|
||||||
@ -467,7 +487,7 @@ class TestOvnNbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase):
|
|||||||
self.assertFalse(self.mech_driver.set_port_status_down.called)
|
self.assertFalse(self.mech_driver.set_port_status_down.called)
|
||||||
|
|
||||||
def test_lsp_down_update_event(self):
|
def test_lsp_down_update_event(self):
|
||||||
new_row_json = {"up": False, "name": "foo-name"}
|
new_row_json = {'up': False, 'enabled': False, 'name': 'foo-name'}
|
||||||
old_row_json = {"up": True}
|
old_row_json = {"up": True}
|
||||||
self._test_lsp_helper('update', new_row_json,
|
self._test_lsp_helper('update', new_row_json,
|
||||||
old_row_json=old_row_json)
|
old_row_json=old_row_json)
|
||||||
@ -476,7 +496,7 @@ class TestOvnNbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase):
|
|||||||
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
||||||
|
|
||||||
def test_lsp_up_update_event_no_old_data(self):
|
def test_lsp_up_update_event_no_old_data(self):
|
||||||
new_row_json = {"up": True, "name": "foo-name"}
|
new_row_json = {'up': True, 'enabled': True, 'name': 'foo-name'}
|
||||||
self._test_lsp_helper('update', new_row_json,
|
self._test_lsp_helper('update', new_row_json,
|
||||||
old_row_json=None)
|
old_row_json=None)
|
||||||
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
self.assertFalse(self.mech_driver.set_port_status_up.called)
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The Neutron ``port.status`` field ("ACTIVE", "DOWN") is now set based on
|
||||||
|
the ML2/OVN Logical Switch Port ``up`` and ``enabled`` flags. The user can
|
||||||
|
now set the ``port.admin_state_up``, that is replicated in the
|
||||||
|
``lsp.enabled`` flag, to enable or disable the port. If the port is
|
||||||
|
disabled, the traffic is stopped and the ``port.status`` is set to "DOWN".
|
Loading…
Reference in New Issue
Block a user