Browse Source

Merge "[OVN] Use the Chassis_Private table for agents healthcheck" into stable/train

changes/32/749832/1
Zuul 3 weeks ago
committed by Gerrit Code Review
parent
commit
4e24f4c5ea
8 changed files with 90 additions and 48 deletions
  1. +40
    -14
      networking_ovn/agent/metadata/agent.py
  2. +4
    -2
      networking_ovn/agent/metadata/ovsdb.py
  3. +15
    -10
      networking_ovn/agent/neutron_agent.py
  4. +17
    -9
      networking_ovn/ml2/mech_driver.py
  5. +0
    -6
      networking_ovn/ovsdb/impl_idl_ovn.py
  6. +2
    -0
      networking_ovn/ovsdb/ovsdb_monitor.py
  7. +2
    -0
      networking_ovn/tests/unit/fakes.py
  8. +10
    -7
      networking_ovn/tests/unit/ml2/test_mech_driver.py

+ 40
- 14
networking_ovn/agent/metadata/agent.py View File

@@ -136,7 +136,7 @@ class PortBindingChassisDeletedEvent(PortBindingChassisEvent):
return False


class ChassisCreateEvent(row_event.RowEvent):
class ChassisCreateEventBase(row_event.RowEvent):
"""Row create event - Chassis name == our_chassis.

On connection, we get a dump of all chassis so if we catch a creation
@@ -144,15 +144,15 @@ class ChassisCreateEvent(row_event.RowEvent):
to do a full sync to make sure that we capture all changes while the
connection to OVSDB was down.
"""
table = None

def __init__(self, metadata_agent):
self.agent = metadata_agent
self.first_time = True
table = 'Chassis'
events = (self.ROW_CREATE,)
super(ChassisCreateEvent, self).__init__(
events, table, (('name', '=', self.agent.chassis),))
self.event_name = 'ChassisCreateEvent'
super(ChassisCreateEventBase, self).__init__(
events, self.table, (('name', '=', self.agent.chassis),))
self.event_name = self.__class__.__name__

def run(self, event, row, old):
if self.first_time:
@@ -166,6 +166,14 @@ class ChassisCreateEvent(row_event.RowEvent):
self.agent.sync()


class ChassisCreateEvent(ChassisCreateEventBase):
table = 'Chassis'


class ChassisPrivateCreateEvent(ChassisCreateEventBase):
table = 'Chassis_Private'


class SbGlobalUpdateEvent(row_event.RowEvent):
"""Row update event on SB_Global table."""

@@ -177,8 +185,12 @@ class SbGlobalUpdateEvent(row_event.RowEvent):
self.event_name = 'SbGlobalUpdateEvent'

def run(self, event, row, old):
self.agent.sb_idl.update_metadata_health_status(
self.agent.chassis, row.nb_cfg).execute()
table = ('Chassis_Private' if self.agent.has_chassis_private
else 'Chassis')
self.agent.sb_idl.db_set(
table, self.agent.chassis, ('external_ids', {
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY:
str(row.nb_cfg)})).execute()


class MetadataAgent(object):
@@ -214,13 +226,26 @@ class MetadataAgent(object):
proxy = metadata_server.UnixDomainMetadataProxy(self.conf)
proxy.run()

tables = ('Encap', 'Port_Binding', 'Datapath_Binding', 'SB_Global',
'Chassis')
events = (PortBindingChassisCreatedEvent(self),
PortBindingChassisDeletedEvent(self),
SbGlobalUpdateEvent(self))

# TODO(lucasagomes): Remove this in the future. Try to register
# the Chassis_Private table, if not present, fallback to the normal
# Chassis table.
# Open the connection to OVN SB database.
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
chassis=self.chassis,
events=[PortBindingChassisCreatedEvent(self),
PortBindingChassisDeletedEvent(self),
ChassisCreateEvent(self),
SbGlobalUpdateEvent(self)]).start()
self.has_chassis_private = False
try:
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
chassis=self.chassis, tables=tables + ('Chassis_Private', ),
events=events + (ChassisPrivateCreateEvent(self), )).start()
self.has_chassis_private = True
except AssertionError:
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
chassis=self.chassis, tables=tables,
events=events + (ChassisCreateEvent(self), )).start()

# Do the initial sync.
self.sync()
@@ -233,9 +258,10 @@ class MetadataAgent(object):
def register_metadata_agent(self):
# NOTE(lucasagomes): db_add() will not overwrite the UUID if
# it's already set.
table = ('Chassis_Private' if self.has_chassis_private else 'Chassis')
ext_ids = {
ovn_const.OVN_AGENT_METADATA_ID_KEY: uuidutils.generate_uuid()}
self.sb_idl.db_add('Chassis', self.chassis, 'external_ids',
self.sb_idl.db_add(table, self.chassis, 'external_ids',
ext_ids).execute(check_error=True)

def _get_own_chassis_name(self):


+ 4
- 2
networking_ovn/agent/metadata/ovsdb.py View File

@@ -38,8 +38,10 @@ class MetadataAgentOvnSbIdl(ovsdb_monitor.OvnIdl):
helper.register_table(table)
super(MetadataAgentOvnSbIdl, self).__init__(
None, connection_string, helper)
if chassis and 'Chassis' in tables:
self.tables['Chassis'].condition = [['name', '==', chassis]]
if chassis:
table = ('Chassis_Private' if 'Chassis_Private' in tables
else 'Chassis')
self.tables[table].condition = [['name', '==', chassis]]
if events:
self.notify_handler.watch_events(events)



+ 15
- 10
networking_ovn/agent/neutron_agent.py View File

@@ -30,8 +30,13 @@ class NeutronAgent(object):
for _type in cls.types:
NeutronAgent.types[_type] = cls

def __init__(self, chassis):
self.chassis = chassis
def __init__(self, chassis_private):
self.chassis_private = chassis_private
try:
self.chassis = self.chassis_private.chassis[0]
except (AttributeError, IndexError):
# No Chassis_Private support, just use Chassis
self.chassis = self.chassis_private

@property
def updated_at(self):
@@ -60,8 +65,8 @@ class NeutronAgent(object):
'admin_state_up': True}

@classmethod
def from_type(cls, _type, chassis):
return cls.types[_type](chassis)
def from_type(cls, _type, chassis_private):
return cls.types[_type](chassis_private)

@staticmethod
def agent_types():
@@ -87,15 +92,15 @@ class ControllerAgent(NeutronAgent):

@property
def nb_cfg(self):
return self.chassis.nb_cfg
return self.chassis_private.nb_cfg

@property
def agent_id(self):
return self.chassis.name
return self.chassis_private.name

@property
def description(self):
return self.chassis.external_ids.get(
return self.chassis_private.external_ids.get(
ovn_const.OVN_AGENT_DESC_KEY, '')


@@ -107,17 +112,17 @@ class MetadataAgent(NeutronAgent):

@property
def nb_cfg(self):
return int(self.chassis.external_ids.get(
return int(self.chassis_private.external_ids.get(
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY, 0))

@property
def agent_id(self):
return self.chassis.external_ids.get(
return self.chassis_private.external_ids.get(
ovn_const.OVN_AGENT_METADATA_ID_KEY)

@property
def description(self):
return self.chassis.external_ids.get(
return self.chassis_private.external_ids.get(
ovn_const.OVN_AGENT_METADATA_DESC_KEY, '')




+ 17
- 9
networking_ovn/ml2/mech_driver.py View File

@@ -119,6 +119,9 @@ class OVNMechanismDriver(api.MechanismDriver):
self.subscribe()
self.qos_driver = qos_driver.OVNQosDriver.create(self)
self.trunk_driver = trunk_driver.OVNTrunkDriver.create(self)
# The agent_chassis_table will be changed to Chassis_Private if it
# exists, we need to have a connection in order to check that.
self.agent_chassis_table = 'Chassis'

@property
def _plugin(self):
@@ -229,6 +232,9 @@ class OVNMechanismDriver(api.MechanismDriver):
self._nb_ovn, self._sb_ovn = impl_idl_ovn.get_ovn_idls(
self, trigger, binding_events=not is_maintenance)

if self._sb_ovn.is_table_present('Chassis_Private'):
self.agent_chassis_table = 'Chassis_Private'

# AGENTS must be populated after fork so if ovn-controller is stopped
# before a worker handles a get_agents request, we still show agents
populate_agents(self)
@@ -1015,14 +1021,15 @@ class OVNMechanismDriver(api.MechanismDriver):
def mark_agent_alive(self, agent):
# Update the time of our successful check
value = timeutils.utcnow(with_timezone=True).isoformat()
self._sb_ovn.db_set('Chassis', agent.chassis.uuid,
('external_ids', {agent.key: value})).execute(
check_error=True)
self._sb_ovn.db_set(
self.agent_chassis_table, agent.chassis_private.uuid,
('external_ids', {agent.key: value})).execute(check_error=True)

def agents_from_chassis(self, chassis, update_db=True):
def agents_from_chassis(self, chassis_private, update_db=True):
agent_dict = {}
# Iterate over each unique Agent subclass
for agent in [a(chassis) for a in n_agent.NeutronAgent.agent_types()]:
for agent in [a(chassis_private)
for a in n_agent.NeutronAgent.agent_types()]:
if not agent.agent_id:
continue
alive = self.agent_alive(agent, update_db)
@@ -1096,7 +1103,7 @@ class OVNMechanismDriver(api.MechanismDriver):


def populate_agents(driver):
for ch in driver._sb_ovn.tables['Chassis'].rows.values():
for ch in driver._sb_ovn.tables[driver.agent_chassis_table].rows.values():
# update the cache, rows are hashed on uuid but it is the name that
# stays consistent across ovn-controller restarts
AGENTS.update({ch.name: ch})
@@ -1117,11 +1124,12 @@ def get_agents(self, context, filters=None, fields=None, _driver=None):
def get_agent(self, context, id, fields=None, _driver=None):
chassis = None
try:
# look up Chassis by *name*, which the id attribte is
chassis = _driver._sb_ovn.lookup('Chassis', id)
# look up Chassis by *name*, which the id attribute is
chassis = _driver._sb_ovn.lookup(_driver.agent_chassis_table, id)
except idlutils.RowNotFound:
# If the UUID is not found, check for the metadata agent ID
for ch in _driver._sb_ovn.tables['Chassis'].rows.values():
for ch in _driver._sb_ovn.tables[
_driver.agent_chassis_table].rows.values():
metadata_agent_id = ch.external_ids.get(
ovn_const.OVN_AGENT_METADATA_ID_KEY)
if id == metadata_agent_id:


+ 0
- 6
networking_ovn/ovsdb/impl_idl_ovn.py View File

@@ -813,12 +813,6 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend):
if (r.mac and str(r.datapath.uuid) == network) and
ip_address in r.mac[0].split(' ')]

def update_metadata_health_status(self, chassis, nb_cfg):
return cmd.UpdateChassisExtIdsCommand(
self, chassis,
{ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: str(nb_cfg)},
if_exists=True)

def set_port_cidrs(self, name, cidrs):
# TODO(twilson) add if_exists to db commands
return self.db_set('Port_Binding', name, 'external_ids',


+ 2
- 0
networking_ovn/ovsdb/ovsdb_monitor.py View File

@@ -520,6 +520,8 @@ class OvnSbIdl(OvnIdlDistributedLock):
def from_server(cls, connection_string, schema_name, driver):
_check_and_set_ssl_files(schema_name)
helper = idlutils.get_schema_helper(connection_string, schema_name)
if 'Chassis_Private' in helper.schema_json['tables']:
helper.register_table('Chassis_Private')
helper.register_table('Chassis')
helper.register_table('Encap')
helper.register_table('Port_Binding')


+ 2
- 0
networking_ovn/tests/unit/fakes.py View File

@@ -165,6 +165,8 @@ class FakeOvsdbSbOvnIdl(object):
self.is_col_present.return_value = False
self.lookup = mock.MagicMock()
self.chassis_list = mock.MagicMock()
self.is_table_present = mock.Mock()
self.is_table_present.return_value = False


class FakeOvsdbTransaction(object):


+ 10
- 7
networking_ovn/tests/unit/ml2/test_mech_driver.py View File

@@ -1495,18 +1495,21 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):

def _add_chassis_agent(self, nb_cfg, agent_type, updated_at=None):
updated_at = updated_at or datetime.datetime.utcnow()
chassis = mock.Mock()
chassis.nb_cfg = nb_cfg
chassis.uuid = uuid.uuid4()
chassis.external_ids = {ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY:
datetime.datetime.isoformat(updated_at)}
chassis_private = mock.Mock()
chassis_private.nb_cfg = nb_cfg
chassis_private.uuid = uuid.uuid4()
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:
chassis.external_ids.update({
chassis_private.external_ids.update({
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: nb_cfg,
ovn_const.METADATA_LIVENESS_CHECK_EXT_ID_KEY:
datetime.datetime.isoformat(updated_at)})
chassis_private.chassis = [chassis_private]

return neutron_agent.NeutronAgent.from_type(agent_type, chassis)
return neutron_agent.NeutronAgent.from_type(
agent_type, chassis_private)

def test_agent_alive_true(self):
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,


Loading…
Cancel
Save