[ovn]support read chassis update time from nb_cfg_timestamp
nb_cfg_timestamp: The timestamp when ovn-controller finishes processing the change corresponding to nb_cfg[1]. it can better reflect the status of chassis. This patch updated some unit tests. ensure mock 'time.time' is stopped after test. if not stop, may affect "timeutils.utcnow_ts" to obtain the real time, cause test case 'test_agent_with_nb_cfg_timestamp_not_timeout' failure. Partial-bug: #1938478 [1] https://www.ovn.org/support/dist-docs/ovn-sb.5.html Change-Id: Ia74a9404411862dc88b48c4a198d5c53f5f52704
This commit is contained in:
parent
a4a2d91b0e
commit
9e263dcf00
@ -13,6 +13,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
import datetime
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
@ -43,7 +44,21 @@ class NeutronAgent(abc.ABC):
|
|||||||
|
|
||||||
def update(self, chassis_private, updated_at=None, clear_down=False):
|
def update(self, chassis_private, updated_at=None, clear_down=False):
|
||||||
self.chassis_private = chassis_private
|
self.chassis_private = chassis_private
|
||||||
self.updated_at = updated_at or timeutils.utcnow(with_timezone=True)
|
if not updated_at:
|
||||||
|
# When use the Chassis_Private table for agents health check,
|
||||||
|
# chassis_private has attribute nb_cfg_timestamp.
|
||||||
|
# nb_cfg_timestamp: the timestamp when ovn-controller finishes
|
||||||
|
# processing the change corresponding to nb_cfg(
|
||||||
|
# https://www.ovn.org/support/dist-docs/ovn-sb.5.html).
|
||||||
|
# it can better reflect the status of chassis.
|
||||||
|
# nb_cfg_timestamp is milliseconds, need to convert to datetime.
|
||||||
|
if hasattr(chassis_private, 'nb_cfg_timestamp'):
|
||||||
|
updated_at = datetime.datetime.fromtimestamp(
|
||||||
|
chassis_private.nb_cfg_timestamp / 1000,
|
||||||
|
datetime.timezone.utc)
|
||||||
|
else:
|
||||||
|
updated_at = timeutils.utcnow(with_timezone=True)
|
||||||
|
self.updated_at = updated_at
|
||||||
if clear_down:
|
if clear_down:
|
||||||
self.set_down = False
|
self.set_down = False
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ from oslo_config import cfg
|
|||||||
from oslo_db import exception as os_db_exc
|
from oslo_db import exception as os_db_exc
|
||||||
from oslo_db.sqlalchemy import provision
|
from oslo_db.sqlalchemy import provision
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
@ -450,9 +451,11 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase,
|
|||||||
name, ['geneve'], '172.24.4.%d' % self._counter,
|
name, ['geneve'], '172.24.4.%d' % self._counter,
|
||||||
external_ids=external_ids, hostname=host).execute(check_error=True)
|
external_ids=external_ids, hostname=host).execute(check_error=True)
|
||||||
if self.sb_api.is_table_present('Chassis_Private'):
|
if self.sb_api.is_table_present('Chassis_Private'):
|
||||||
|
nb_cfg_timestamp = timeutils.utcnow_ts() * 1000
|
||||||
self.sb_api.db_create(
|
self.sb_api.db_create(
|
||||||
'Chassis_Private', name=name, external_ids=external_ids,
|
'Chassis_Private', name=name, external_ids=external_ids,
|
||||||
chassis=chassis.uuid).execute(check_error=True)
|
chassis=chassis.uuid, nb_cfg_timestamp=nb_cfg_timestamp
|
||||||
|
).execute(check_error=True)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def del_fake_chassis(self, chassis, if_exists=True):
|
def del_fake_chassis(self, chassis, if_exists=True):
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
@ -21,6 +21,7 @@ from neutron_lib.api.definitions import portbindings
|
|||||||
from neutron_lib.plugins import constants as plugin_constants
|
from neutron_lib.plugins import constants as plugin_constants
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
from ovsdbapp.backend.ovs_idl import event
|
from ovsdbapp.backend.ovs_idl import event
|
||||||
from ovsdbapp.backend.ovs_idl import idlutils
|
from ovsdbapp.backend.ovs_idl import idlutils
|
||||||
@ -403,6 +404,21 @@ class TestAgentMonitor(base.TestOVNFunctionalBase):
|
|||||||
self.assertEqual(neutron_agent.ControllerAgent,
|
self.assertEqual(neutron_agent.ControllerAgent,
|
||||||
type(neutron_agent.AgentCache()[self.chassis_name]))
|
type(neutron_agent.AgentCache()[self.chassis_name]))
|
||||||
|
|
||||||
|
def test_agent_updated_at_use_nb_cfg_timestamp(self):
|
||||||
|
if not self.sb_api.is_table_present('Chassis_Private'):
|
||||||
|
self.skipTest('Ovn sb not support Chassis_Private')
|
||||||
|
timestamp = timeutils.utcnow_ts()
|
||||||
|
nb_cfg_timestamp = timestamp * 1000
|
||||||
|
updated_at = datetime.datetime.fromtimestamp(
|
||||||
|
timestamp, datetime.timezone.utc)
|
||||||
|
self.sb_api.db_set('Chassis_Private', self.chassis_name, (
|
||||||
|
'nb_cfg_timestamp', nb_cfg_timestamp)).execute(check_error=True)
|
||||||
|
n_utils.wait_until_true(lambda:
|
||||||
|
neutron_agent.AgentCache()[self.chassis_name].
|
||||||
|
chassis_private.nb_cfg_timestamp == nb_cfg_timestamp)
|
||||||
|
agent = neutron_agent.AgentCache()[self.chassis_name]
|
||||||
|
self.assertEqual(updated_at, agent.updated_at)
|
||||||
|
|
||||||
|
|
||||||
class TestOvnIdlProbeInterval(base.TestOVNFunctionalBase):
|
class TestOvnIdlProbeInterval(base.TestOVNFunctionalBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -95,7 +95,10 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase):
|
|||||||
|
|
||||||
expect_ident = (
|
expect_ident = (
|
||||||
b'\x00\x01\x00\x01\x00\x00\x00\x01\x00\x01\x02\x03\x04\x05')
|
b'\x00\x01\x00\x01\x00\x00\x00\x01\x00\x01\x02\x03\x04\x05')
|
||||||
time.time = mock.Mock(return_value=ONE_SEC_AFTER_2000)
|
time_p = mock.patch.object(time, 'time',
|
||||||
|
return_value=ONE_SEC_AFTER_2000)
|
||||||
|
time_p.start()
|
||||||
|
self.addCleanup(time_p.stop)
|
||||||
client_ident = client_ident = (
|
client_ident = client_ident = (
|
||||||
self.dhcp6_responer.get_dhcpv6_client_ident(
|
self.dhcp6_responer.get_dhcpv6_client_ident(
|
||||||
self.port_info['mac_address'], []))
|
self.port_info['mac_address'], []))
|
||||||
@ -164,7 +167,10 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase):
|
|||||||
option_list=option_list,
|
option_list=option_list,
|
||||||
options_len=0)
|
options_len=0)
|
||||||
|
|
||||||
time.time = mock.Mock(return_value=ONE_SEC_AFTER_2000)
|
time_p = mock.patch.object(time, 'time',
|
||||||
|
return_value=ONE_SEC_AFTER_2000)
|
||||||
|
time_p.start()
|
||||||
|
self.addCleanup(time_p.stop)
|
||||||
packet_in = self._create_test_dhcp6_packet(zero_time=zero_time)
|
packet_in = self._create_test_dhcp6_packet(zero_time=zero_time)
|
||||||
pkt_dhcp = packet_in.get_protocol(dhcp6.dhcp6)
|
pkt_dhcp = packet_in.get_protocol(dhcp6.dhcp6)
|
||||||
dhcp_req_state = dhcp_ipv6.DHCPV6_TYPE_MAP.get(pkt_dhcp.msg_type)
|
dhcp_req_state = dhcp_ipv6.DHCPV6_TYPE_MAP.get(pkt_dhcp.msg_type)
|
||||||
@ -192,7 +198,10 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase):
|
|||||||
mac = '00:01:02:03:04:05'
|
mac = '00:01:02:03:04:05'
|
||||||
packet_in = self._create_test_dhcp6_packet()
|
packet_in = self._create_test_dhcp6_packet()
|
||||||
header_dhcp = packet_in.get_protocol(dhcp6.dhcp6)
|
header_dhcp = packet_in.get_protocol(dhcp6.dhcp6)
|
||||||
time.time = mock.Mock(return_value=ONE_SEC_AFTER_2000)
|
time_p = mock.patch.object(time, 'time',
|
||||||
|
return_value=ONE_SEC_AFTER_2000)
|
||||||
|
time_p.start()
|
||||||
|
self.addCleanup(time_p.stop)
|
||||||
dhcp_options = self.dhcp6_responer.get_reply_dhcp_options(
|
dhcp_options = self.dhcp6_responer.get_reply_dhcp_options(
|
||||||
mac, message="all addresses still on link",
|
mac, message="all addresses still on link",
|
||||||
req_options=header_dhcp.options.option_list)
|
req_options=header_dhcp.options.option_list)
|
||||||
|
@ -2080,11 +2080,15 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
|||||||
|
|
||||||
def _add_chassis_agent(self, nb_cfg, agent_type, chassis_private=None,
|
def _add_chassis_agent(self, nb_cfg, agent_type, chassis_private=None,
|
||||||
updated_at=None):
|
updated_at=None):
|
||||||
updated_at = updated_at or timeutils.utcnow(with_timezone=True)
|
|
||||||
chassis_private = chassis_private or self._add_chassis(nb_cfg)
|
chassis_private = chassis_private or self._add_chassis(nb_cfg)
|
||||||
chassis_private.external_ids = {
|
if hasattr(chassis_private, 'nb_cfg_timestamp') and isinstance(
|
||||||
ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY:
|
chassis_private.nb_cfg_timestamp, mock.Mock):
|
||||||
datetime.datetime.isoformat(updated_at)}
|
del chassis_private.nb_cfg_timestamp
|
||||||
|
chassis_private.external_ids = {}
|
||||||
|
if updated_at:
|
||||||
|
chassis_private.external_ids[
|
||||||
|
ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY] = \
|
||||||
|
datetime.datetime.isoformat(updated_at)
|
||||||
if agent_type == ovn_const.OVN_METADATA_AGENT:
|
if agent_type == ovn_const.OVN_METADATA_AGENT:
|
||||||
chassis_private.external_ids.update({
|
chassis_private.external_ids.update({
|
||||||
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: nb_cfg,
|
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: nb_cfg,
|
||||||
@ -2141,6 +2145,33 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
|||||||
self.assertFalse(agent.alive, "Agent of type %s alive=%s" %
|
self.assertFalse(agent.alive, "Agent of type %s alive=%s" %
|
||||||
(agent.agent_type, agent.alive))
|
(agent.agent_type, agent.alive))
|
||||||
|
|
||||||
|
def test_agent_with_nb_cfg_timestamp_timeout(self):
|
||||||
|
nb_cfg = 3
|
||||||
|
chassis_private = self._add_chassis(nb_cfg)
|
||||||
|
|
||||||
|
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + 2
|
||||||
|
updated_at = (timeutils.utcnow_ts() - cfg.CONF.agent_down_time - 1
|
||||||
|
) * 1000
|
||||||
|
chassis_private.nb_cfg_timestamp = updated_at
|
||||||
|
agent_type = ovn_const.OVN_CONTROLLER_AGENT
|
||||||
|
agent = self._add_chassis_agent(nb_cfg, agent_type,
|
||||||
|
chassis_private)
|
||||||
|
self.assertFalse(agent.alive, "Agent of type %s alive=%s" %
|
||||||
|
(agent.agent_type, agent.alive))
|
||||||
|
|
||||||
|
def test_agent_with_nb_cfg_timestamp_not_timeout(self):
|
||||||
|
nb_cfg = 3
|
||||||
|
chassis_private = self._add_chassis(nb_cfg)
|
||||||
|
|
||||||
|
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + 2
|
||||||
|
updated_at = timeutils.utcnow_ts() * 1000
|
||||||
|
chassis_private.nb_cfg_timestamp = updated_at
|
||||||
|
agent_type = ovn_const.OVN_CONTROLLER_AGENT
|
||||||
|
agent = self._add_chassis_agent(nb_cfg, agent_type,
|
||||||
|
chassis_private)
|
||||||
|
self.assertTrue(agent.alive, "Agent of type %s alive=%s" % (
|
||||||
|
agent.agent_type, agent.alive))
|
||||||
|
|
||||||
def _test__update_dnat_entry_if_needed(self, up=True):
|
def _test__update_dnat_entry_if_needed(self, up=True):
|
||||||
ovn_conf.cfg.CONF.set_override(
|
ovn_conf.cfg.CONF.set_override(
|
||||||
'enable_distributed_floating_ip', True, group='ovn')
|
'enable_distributed_floating_ip', True, group='ovn')
|
||||||
|
Loading…
Reference in New Issue
Block a user