blob: 1d99a03f60f814494eeb72cbe9c19293a8a46936 [file]
"""
mock_mctpd.py - Mock server for mctpd and Entity Manager in MctpUtil tests.
This script simulates:
1. mctpd emitting InterfacesAdded signals for MCTP endpoints.
2. Entity Manager responding to GetManagedObjects queries.
It also provides a control interface to trigger events from the test:
- TriggerAdd(endpoint_path, em_config_path)
- TriggerRemove(endpoint_path)
To support precise synchronization without modifying product code, this mock
server emits custom D-Bus signals on the 'com.example.Control' interface:
- QueryStarted: Emitted when GetManagedObjects is called.
- QueryCompleted: Emitted when GetManagedObjects returns.
Usage in tests:
1. Start this script as a subprocess in SetUp().
2. Use `busctl call` to trigger events via TriggerAdd/TriggerRemove.
3. Listen for QueryStarted/QueryCompleted to synchronize test steps.
"""
import dbus
import dbus.service
import dbus.mainloop.glib
from gi.repository import GLib
import sys
import time
import signal
class MockEndpoint(dbus.service.Object):
def __init__(self, bus, path, em_config_path):
dbus.service.Object.__init__(self, bus, path)
self.associations = dbus.Array([
dbus.Struct(('configured_by', 'endpoint', em_config_path), signature='(sss)')
], signature='(sss)')
@dbus.service.method('org.freedesktop.DBus.Properties', in_signature='ss', out_signature='v')
def Get(self, interface_name, property_name):
if interface_name == 'xyz.openbmc_project.Association.Definitions' and property_name == 'Associations':
return dbus.LowLevel.Variant(self.associations, 'a(sss)')
raise dbus.exceptions.DBusException('org.freedesktop.DBus.Error.InvalidArgs')
@dbus.service.method('org.freedesktop.DBus.Properties', in_signature='s', out_signature='a{sv}')
def GetAll(self, interface_name):
if interface_name == 'xyz.openbmc_project.Association.Definitions':
return {'Associations': dbus.LowLevel.Variant(self.associations, 'a(sss)')}
return {}
class MockTarget(dbus.service.Object):
def __init__(self, bus):
dbus.service.Object.__init__(self, bus, '/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device')
@dbus.service.method('org.freedesktop.DBus.Properties', in_signature='s', out_signature='a{sv}')
def GetAll(self, interface_name):
if interface_name == 'xyz.openbmc_project.Configuration.BusInfo':
return {
'BusType': 'USB',
'Port': '1.2.5',
'Configuration': dbus.UInt32(1)
}
return {}
class MockMctpd(dbus.service.Object):
def __init__(self, bus):
dbus.service.Object.__init__(self, bus, '/au/com/codeconstruct/mctp1')
self.bus = bus
self.endpoints = {}
@dbus.service.signal('org.freedesktop.DBus.ObjectManager', signature='oa{sa{sv}}')
def InterfacesAdded(self, object_path, interfaces_and_properties):
print(f"Emitting InterfacesAdded for {object_path}")
sys.stdout.flush()
@dbus.service.signal('org.freedesktop.DBus.ObjectManager', signature='oas')
def InterfacesRemoved(self, object_path, interfaces):
pass
@dbus.service.method('com.example.Control', in_signature='ss')
def TriggerAdd(self, endpoint_path, em_config_path):
if endpoint_path not in self.endpoints:
self.endpoints[endpoint_path] = MockEndpoint(self.bus, endpoint_path, em_config_path)
self.InterfacesAdded(dbus.ObjectPath(endpoint_path), {
'xyz.openbmc_project.Association.Definitions': {
'Associations': dbus.Array([
dbus.Struct(('configured_by', 'endpoint', em_config_path), signature='(sss)')
], signature='(sss)')
}
})
return "OK"
@dbus.service.method('com.example.Control', in_signature='s')
def TriggerRemove(self, endpoint_path):
print("TriggerRemove called for " + endpoint_path)
sys.stdout.flush()
if endpoint_path in self.endpoints:
self.InterfacesRemoved(dbus.ObjectPath(endpoint_path), ['xyz.openbmc_project.Association.Definitions'])
self.endpoints[endpoint_path].remove_from_connection()
del self.endpoints[endpoint_path]
return "OK"
return "Not Found"
class MockEM(dbus.service.Object):
def __init__(self, bus):
dbus.service.Object.__init__(self, bus, '/xyz/openbmc_project/inventory')
@dbus.service.signal('com.example.Control', signature='')
def QueryStarted(self):
pass
@dbus.service.signal('com.example.Control', signature='')
def QueryCompleted(self):
pass
@dbus.service.method('org.freedesktop.DBus.ObjectManager', out_signature='a{oa{sa{sv}}}')
def GetManagedObjects(self):
self.QueryStarted()
print("GetManagedObjects called, delaying reply...")
sys.stdout.flush()
time.sleep(1)
self.QueryCompleted()
return {
dbus.ObjectPath('/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device'): {
'xyz.openbmc_project.Configuration.BusInfo': {
'BusType': 'USB',
'Port': '1.2.5',
'Configuration': dbus.UInt32(1)
}
}
}
def main():
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
import os
if 'DBUS_SESSION_BUS_ADDRESS' in os.environ:
bus = dbus.SessionBus()
else:
bus = dbus.SystemBus()
loop = GLib.MainLoop()
def signal_handler(sig, frame):
print("Received SIGTERM, shutting down gracefully...")
loop.quit()
signal.signal(signal.SIGTERM, signal_handler)
_target = MockTarget(bus) # noqa: F841
_mctpd = MockMctpd(bus) # noqa: F841
_em = MockEM(bus) # noqa: F841
_name_em = dbus.service.BusName("xyz.openbmc_project.EntityManager", bus) # noqa: F841
_name_mctp = dbus.service.BusName("xyz.openbmc_project.Mctp", bus) # noqa: F841
print("Mock mctpd and EM running...")
sys.stdout.flush()
loop.run()
if __name__ == '__main__':
main()