diff options
Diffstat (limited to 'mpm')
| -rw-r--r-- | mpm/CMakeLists.txt | 50 | ||||
| -rw-r--r-- | mpm/python/CMakeLists.txt | 23 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/__init__.py.in (renamed from mpm/python/usrp_mpm/__init__.py) | 18 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/periph_manager/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/periph_manager/sim.py | 302 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/sys_utils/net.py | 10 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/xports/xportmgr_udp.py | 14 | 
8 files changed, 376 insertions, 44 deletions
| diff --git a/mpm/CMakeLists.txt b/mpm/CMakeLists.txt index a30d3acd9..e426716a6 100644 --- a/mpm/CMakeLists.txt +++ b/mpm/CMakeLists.txt @@ -119,7 +119,7 @@ if(_has_no_psabi)      message(STATUS "Disabling psABI warnings.")      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi")  endif(_has_no_psabi) -set(MPM_ALL_DEVICES n3xx e320 e31x tests) +set(MPM_ALL_DEVICES n3xx e320 e31x sim tests)  set(MPM_DEVICE "n3xx" CACHE STRING "Choose an MPM device to build")  set_property(CACHE MPM_DEVICE PROPERTY STRINGS ${MPM_ALL_DEVICES})  # Validate MPM_DEVICE @@ -137,6 +137,9 @@ elseif(MPM_DEVICE STREQUAL "e320")      set(ENABLE_E320 ON)  elseif(MPM_DEVICE STREQUAL "e31x")      set(ENABLE_E300 ON) +elseif(MPM_DEVICE STREQUAL "sim") +    set(ENABLE_SIM TRUE) +    set(ENABLE_LIBMPM OFF)  endif()  MPM_REGISTER_COMPONENT("LibMPM" ENABLE_LIBMPM ON "Boost_FOUND" OFF ON) @@ -145,33 +148,38 @@ MPM_REGISTER_COMPONENT("Magnesium" ENABLE_MAGNESIUM OFF "ENABLE_MYKONOS" OFF OFF  MPM_REGISTER_COMPONENT("E320" ENABLE_E320 OFF "ENABLE_LIBMPM" OFF OFF)  MPM_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBMPM" OFF OFF) -add_subdirectory(include) -include_directories( -    ${CMAKE_CURRENT_SOURCE_DIR}/include -    ${CMAKE_BINARY_DIR}/include -    ${UHD_HOST_ROOT}/include -) +if(NOT ENABLE_SIM) +    add_subdirectory(include) +    include_directories( +        ${CMAKE_CURRENT_SOURCE_DIR}/include +        ${CMAKE_BINARY_DIR}/include +        ${UHD_HOST_ROOT}/include +    ) -add_subdirectory(lib) +    add_subdirectory(lib) -message("usrp_periphs objects: ${usrp_periphs_objects}") -add_library(usrp-periphs SHARED ${usrp_periphs_objects}) -target_link_libraries(usrp-periphs -    udev -    ${Boost_LIBRARIES} -) +    message("usrp_periphs objects: ${usrp_periphs_objects}") +    add_library(usrp-periphs SHARED ${usrp_periphs_objects}) +    target_link_libraries(usrp-periphs +        udev +        ${Boost_LIBRARIES} +    ) -install(TARGETS usrp-periphs LIBRARY DESTINATION ${LIBRARY_DIR} COMPONENT libraries) +    install(TARGETS usrp-periphs LIBRARY DESTINATION ${LIBRARY_DIR} COMPONENT libraries) -# TODO: Come up with a versioning scheme for the MPM ABI. Not high priority -# though... we're the only ones linking against that. -set_target_properties(usrp-periphs PROPERTIES VERSION "${MPM_VERSION_MAJOR}.${MPM_VERSION_API}.${MPM_VERSION_ABI}") -set_target_properties(usrp-periphs PROPERTIES SOVERSION ${MPM_VERSION_MAJOR}) +    # TODO: Come up with a versioning scheme for the MPM ABI. Not high priority +    # though... we're the only ones linking against that. +    set_target_properties(usrp-periphs PROPERTIES VERSION "${MPM_VERSION_MAJOR}.${MPM_VERSION_API}.${MPM_VERSION_ABI}") +    set_target_properties(usrp-periphs PROPERTIES SOVERSION ${MPM_VERSION_MAJOR}) + +    add_subdirectory(systemd) +    add_subdirectory(tools) +endif(NOT ENABLE_SIM)  enable_testing()  add_subdirectory(python) -add_subdirectory(tools) -add_subdirectory(systemd) + +  ########################################################################  # Print Summary diff --git a/mpm/python/CMakeLists.txt b/mpm/python/CMakeLists.txt index 5691a67a2..b1bc9fa16 100644 --- a/mpm/python/CMakeLists.txt +++ b/mpm/python/CMakeLists.txt @@ -16,15 +16,17 @@ elseif(MPM_DEVICE STREQUAL "e31x")      add_library(pyusrp_periphs SHARED pyusrp_periphs/e31x/pyusrp_periphs.cpp)  endif(MPM_DEVICE STREQUAL "n3xx") -target_include_directories(pyusrp_periphs PUBLIC -    ${PYTHON_INCLUDE_DIRS} -    ${CMAKE_SOURCE_DIR}/lib/ -    ${UHD_HOST_ROOT}/lib/usrp/common -    ${UHD_HOST_ROOT}/lib/usrp/common/ad9361_driver -    ${UHD_HOST_ROOT}/lib/deps/pybind11/include -) -target_link_libraries(pyusrp_periphs ${Boost_LIBRARIES} usrp-periphs) -add_custom_command(TARGET pyusrp_periphs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libpyusrp_periphs.so ${CMAKE_CURRENT_BINARY_DIR}/usrp_mpm/libpyusrp_periphs.so) +if(NOT ENABLE_SIM) +    target_include_directories(pyusrp_periphs PUBLIC +        ${PYTHON_INCLUDE_DIRS} +        ${CMAKE_SOURCE_DIR}/lib/ +        ${UHD_HOST_ROOT}/lib/usrp/common +        ${UHD_HOST_ROOT}/lib/usrp/common/ad9361_driver +        ${UHD_HOST_ROOT}/lib/deps/pybind11/include +    ) +    target_link_libraries(pyusrp_periphs ${Boost_LIBRARIES} usrp-periphs) +    add_custom_command(TARGET pyusrp_periphs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libpyusrp_periphs.so ${CMAKE_CURRENT_BINARY_DIR}/usrp_mpm/libpyusrp_periphs.so) +endif(NOT ENABLE_SIM)  set(USRP_MPM_FILES "")  add_subdirectory(usrp_mpm) @@ -34,9 +36,12 @@ set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in")  set(SETUP_PY    "${CMAKE_CURRENT_BINARY_DIR}/setup.py")  set(PERIPH_MGR_INIT_IN "${CMAKE_CURRENT_SOURCE_DIR}/usrp_mpm/periph_manager/__init__.py.in")  set(PERIPH_MGR_INIT "${CMAKE_CURRENT_BINARY_DIR}/usrp_mpm/periph_manager/__init__.py") +set(USRP_MPM_INIT_IN "${CMAKE_CURRENT_SOURCE_DIR}/usrp_mpm/__init__.py.in") +set(USRP_MPM_INIT "${CMAKE_CURRENT_BINARY_DIR}/usrp_mpm/__init__.py")  configure_file(${SETUP_PY_IN} ${SETUP_PY})  configure_file(${PERIPH_MGR_INIT_IN} ${PERIPH_MGR_INIT}) +configure_file(${USRP_MPM_INIT_IN} ${USRP_MPM_INIT})  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/usrp_hwd.py" "${CMAKE_CURRENT_BINARY_DIR}/usrp_hwd.py" COPYONLY)  add_custom_command(OUTPUT ${OUTPUT} diff --git a/mpm/python/usrp_mpm/CMakeLists.txt b/mpm/python/usrp_mpm/CMakeLists.txt index 9ea114e0b..93c140a8d 100644 --- a/mpm/python/usrp_mpm/CMakeLists.txt +++ b/mpm/python/usrp_mpm/CMakeLists.txt @@ -9,7 +9,7 @@  ########################################################################  set(USRP_MPM_FILES ${USRP_MPM_FILES})  set(USRP_MPM_TOP_FILES -    ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py +    ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in      ${CMAKE_CURRENT_SOURCE_DIR}/aurora_control.py      ${CMAKE_CURRENT_SOURCE_DIR}/bfrfs.py      ${CMAKE_CURRENT_SOURCE_DIR}/components.py diff --git a/mpm/python/usrp_mpm/__init__.py b/mpm/python/usrp_mpm/__init__.py.in index a8efbd498..c2134d092 100644 --- a/mpm/python/usrp_mpm/__init__.py +++ b/mpm/python/usrp_mpm/__init__.py.in @@ -7,18 +7,24 @@  MPM Module  """ -from . import libpyusrp_periphs as lib +__simulated__ = ("${MPM_DEVICE}" == "sim") +  from .discovery import spawn_discovery_process  from .rpc_server import spawn_rpc_process  from . import mpmtypes -from . import periph_manager -from . import dboard_manager -from . import xports -from . import cores -from . import chips  from . import gpsd_iface  from .mpmlog import get_main_logger +if not __simulated__: +    from . import libpyusrp_periphs as lib +    from . import periph_manager +    from . import dboard_manager +    from . import xports +    from . import cores +    from . import chips +else: +    from . import periph_manager +  __version__ = periph_manager.__version__  __githash__ = periph_manager.__githash__  __mpm_device__ = periph_manager.__mpm_device__ diff --git a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt index 4f8520c12..747b8967a 100644 --- a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt @@ -18,6 +18,7 @@ set(USRP_MPM_PERIPHMGR_FILES      ${CMAKE_CURRENT_SOURCE_DIR}/e320_periphs.py      ${CMAKE_CURRENT_SOURCE_DIR}/e31x.py      ${CMAKE_CURRENT_SOURCE_DIR}/e31x_periphs.py +    ${CMAKE_CURRENT_SOURCE_DIR}/sim.py      )  list(APPEND USRP_MPM_FILES ${USRP_MPM_PERIPHMGR_FILES})  set(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE) diff --git a/mpm/python/usrp_mpm/periph_manager/sim.py b/mpm/python/usrp_mpm/periph_manager/sim.py new file mode 100644 index 000000000..00ba5ba38 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/sim.py @@ -0,0 +1,302 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +usrp simulation module + +This module is used to emulate a usrp when running on a standard +computer. You can build mpm in this configuration by using the cmake +flag -DMPM_DEVICE=sim +""" + +from pyroute2 import IPRoute +from usrp_mpm.xports import XportMgrUDP +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.rpc_server import no_claim +from usrp_mpm.periph_manager import PeriphManagerBase + +CLOCK_SOURCE_INTERNAL = "internal" + +E320_DBOARD_SLOT_IDX = 0 + +class SimXportMgrUDP(XportMgrUDP): +    """This is an adaptor class for the normal XportMgrUDP +    In radios, the interface names are hardcoded. Since we are on a +    desktop computer, we generate the names at runtime. +    """ +    def __init__(self, log, args, eth_dispatcher_cls): +        with IPRoute() as ipr: +            self.iface_config = { +                link.get_attr('IFLA_IFNAME'): { +                    'label': link.get_attr('IFLA_IFNAME'), +                    'type': 'forward' +                } for link in ipr.get_links() +            } +        super().__init__(log, args, eth_dispatcher_cls) + +class SimEthDispatcher: +    """This is the hardware specific part of the normal XportMgrUDP +    that we have to simulate. We get the ipv4 addr with IPRoute +    instead of registers +    """ +    DEFAULT_VITA_PORT = (49153, 49154) +    LOG = None + +    def __init__(self, if_name): +        self.log = get_logger(if_name) +        self.if_name = if_name + +    def set_ipv4_addr(self, addr): +        """This doesn't actually change the ipv4 address, it just +        checks to make sure the requested address is already our +        address, and complains otherwise. +        """ +        with IPRoute() as ipr: +            valid_iface_idx = ipr.link_lookup(ifname=self.if_name)[0] +            link_info = ipr.get_links(valid_iface_idx)[0] +            real_addr = link_info.get_attr('IFLA_ADDRESS') +            if addr != real_addr: +                self.log.warning("Cannot change ip address on simulator! Requested: {}, Actual: {}" +                                 .format(addr, real_addr)) + +class sim(PeriphManagerBase): +    """This is a periph manager that is designed to run on a regular +    computer rather than the arm core on an SDR +    """ +    ######################################################################### +    # Overridables +    # +    # See PeriphManagerBase for documentation on these fields +    ######################################################################### +    description = "E320-Series Device - SIMULATED" +    pids = {0xE320: 'e320'} + +    mboard_info = {"type": "e3xx", "product": "e320"} +    mboard_max_rev = 7  # RevC +    mboard_sensor_callback_map = {} + +    ########################################################################### +    # Ctor and device initialization tasks +    ########################################################################### +    def __init__(self, args): +        super().__init__() +        self.device_id = 1 + +        # Unlike the real hardware drivers, if there is an exception here, +        # we just crash. No use missing an error when testing. +        self._init_peripherals(args) +        self.init_dboards(args) +        if not args.get('skip_boot_init', False): +            self.init(args) + +    @classmethod +    def generate_device_info(cls, eeprom_md, mboard_info, dboard_infos): +        """ +        Hard-code our product map +        """ +        # Add the default PeriphManagerBase information first +        device_info = super().generate_device_info( +            eeprom_md, mboard_info, dboard_infos) +        # Then add device-specific information +        mb_pid = eeprom_md.get('pid') +        device_info['product'] = cls.pids.get(mb_pid, 'unknown') +        return device_info + +    def _read_mboard_eeprom(self): +        """ +        Read out a simulated mboard eeprom and saves it to the appropriate member variable +        """ +        self._eeprom_head = sim._generate_eeprom_head() + +        self.log.trace("Found EEPROM metadata: '{}'" +                       .format(str(self._eeprom_head))) +        return (self._eeprom_head, None) + +    @staticmethod +    def _generate_eeprom_head(serial=b'3196D2A', rev=2, rev_compat=2): +        return {'pid': 0xE320, +                'rev': rev, +                'rev_compat': rev_compat, +                'serial': serial} + +    def _init_peripherals(self, args): +        """ +        Turn on all peripherals. This may throw an error on failure, so make +        sure to catch it. + +        Peripherals are initialized in the order of least likely to fail, to most +        likely. +        """ +        # Sanity checks +        assert self.mboard_info.get('product') in self.pids.values(), \ +            "Device product could not be determined!" +        # Init peripherals + +        # Init CHDR transports +        self._xport_mgrs = { +            'udp': SimXportMgrUDP(self.log, args, SimEthDispatcher) +        } +        #TODO: Actually create transports here when RFNoC is integrated +        self.log.trace("CHDR transport creation was skipped") + +        # Init complete. +        self.log.debug("Device info: {}".format(self.device_info)) + +    ########################################################################### +    # Device info +    ########################################################################### +    def get_device_info_dyn(self): +        """ +        Append the device info with current IP addresses. +        """ +        if not self._device_initialized: +            return {} +        device_info = self._xport_mgrs['udp'].get_xport_info() +        self.log.warn("get_device_info_dyn() - FPGA functionality not implemented yet") +        return device_info + +    def set_device_id(self, device_id): +        """ +        Sets the device ID for this motherboard. +        The device ID is used to identify the RFNoC components associated with +        this motherboard. +        """ +        self.device_id = device_id + +    def get_device_id(self): +        """ +        Gets the device ID for this motherboard. +        The device ID is used to identify the RFNoC components associated with +        this motherboard. +        """ +        return self.device_id + +    @no_claim +    def get_proto_ver(self): +        """ +        Return RFNoC protocol version +        """ +        return 0x100 + +    @no_claim +    def get_chdr_width(self): +        """ +        Return RFNoC CHDR width +        """ +        return 64 + +    ########################################################################### +    # Transport API +    ########################################################################### +    def get_chdr_link_types(self): +        """ +        This will only ever return a single item (udp). +        """ +        return ["udp"] + +    def get_chdr_link_options(self, xport_type): +        """ +        Returns a list of dictionaries. Every dictionary contains information +        about one way to connect to this device in order to initiate CHDR +        traffic. + +        The interpretation of the return value is very highly dependant on the +        transport type (xport_type). +        For UDP, the every entry of the list has the following keys: +        - ipv4 (IP Address) +        - port (UDP port) +        - link_rate (bps of the link, e.g. 10e9 for 10GigE) +        """ +        if xport_type not in self._xport_mgrs: +            self.log.warning("Can't get link options for unknown link type: '{}'." +                             .format(xport_type)) +            return [] +        return self._xport_mgrs[xport_type].get_chdr_link_options() + +    ####################################################################### +    # Timekeeper API +    ####################################################################### +    def get_master_clock_rate(self): +        """ Return the master clock rate set during init """ +        return self._master_clock_rate + +    def get_num_timekeepers(self): +        """ +        Return the number of timekeepers +        """ +        return 1 + +    def set_timekeeper_time(self, tk_idx, ticks, next_pps): +        """ +        Set the time in ticks + +        Arguments: +        tk_idx: Index of timekeeper +        ticks: Time in ticks +        next_pps: If True, set time at next PPS. Otherwise, set time now. +        """ +        self.log.debug("Setting timekeeper time (tx_idx:{}, ticks: {}, next_pps: {})" +                       .format(tk_idx, ticks, next_pps)) + +    def get_timekeeper_time(self, tk_idx, last_pps): +        """ +        Get the time in ticks + +        Arguments: +        tk_idx: Index of timekeeper +        next_pps: If True, get time at last PPS. Otherwise, get time now. +        """ +        return 0 + +    def set_tick_period(self, tk_idx, period_ns): +        """ +        Set the time per tick in nanoseconds (tick period) + +        Arguments: +        tk_idx: Index of timekeeper +        period_ns: Period in nanoseconds +        """ +        self.log.debug("Setting tick period (tk_idx: {}, period_ns: {})" +                       .format(tk_idx, period_ns)) + +    def get_clocks(self): +        """ +        Gets the RFNoC-related clocks present in the FPGA design +        """ +        return [ +            { +                'name': 'radio_clk', +                'freq': str(122.88e6), +                'mutable': 'true' +            }, +            { +                'name': 'bus_clk', +                'freq': str(200e6), +            }, +            { +                'name': 'ctrl_clk', +                'freq': str(40e6), +            } +        ] + +    def get_time_sources(self): +        " Returns list of valid time sources " +        return (CLOCK_SOURCE_INTERNAL,) + +    def get_clock_sources(self): +        " Lists all available clock sources. " +        return (CLOCK_SOURCE_INTERNAL,) + +    def get_clock_source(self): +        " Returns the current Clock Source " +        return CLOCK_SOURCE_INTERNAL + +    def set_clock_source(self, source): +        " No-op which sets the clock source on a real radio " +        self.log.debug("Setting clock source to {}".format(source)) + +    def set_channel_mode(self, channel_mode): +        " No-op which sets the channel mode on a real radio " +        self.log.debug("Using channel mode {}".format(channel_mode)) diff --git a/mpm/python/usrp_mpm/sys_utils/net.py b/mpm/python/usrp_mpm/sys_utils/net.py index a9be7b87d..6ba5f0c3e 100644 --- a/mpm/python/usrp_mpm/sys_utils/net.py +++ b/mpm/python/usrp_mpm/sys_utils/net.py @@ -31,7 +31,10 @@ def get_valid_interfaces(iface_list):                  continue              valid_iface_idx = valid_iface_idx[0]              link_info = ipr.get_links(valid_iface_idx)[0] -            if link_info.get_attr('IFLA_OPERSTATE') == 'UP' \ +            # IFLA_OPERSTATE attribute isn't implemented on WSL +            # Workaround is ignore it in the simulator +            from usrp_mpm import __simulated__ +            if (link_info.get_attr('IFLA_OPERSTATE') == 'UP' or __simulated__) \                      and len(get_iface_addrs(link_info.get_attr('IFLA_ADDRESS'))):                  assert link_info.get_attr('IFLA_IFNAME') == iface                  valid_ifaces.append(iface) @@ -83,6 +86,11 @@ def get_link_speed(ifname):      The speed is Megabits/sec      (from kernel at https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-net)      """ +    # This wasn't implemented in WSL or in the linux pc I tested it on +    # We will return a sensible default +    from usrp_mpm import __simulated__ +    if __simulated__: +        return 1000      net_sysfs = [device for device in pyudev.Context().list_devices(subsystem='net')                   if device.sys_name == ifname][0] diff --git a/mpm/python/usrp_mpm/xports/xportmgr_udp.py b/mpm/python/usrp_mpm/xports/xportmgr_udp.py index 099a67d2a..197a628e4 100644 --- a/mpm/python/usrp_mpm/xports/xportmgr_udp.py +++ b/mpm/python/usrp_mpm/xports/xportmgr_udp.py @@ -8,10 +8,10 @@  UDP Transport manager  """ +import importlib  import subprocess  from six import iteritems, itervalues  from usrp_mpm import prefs -from usrp_mpm.ethdispatch import EthDispatcherCtrl  from usrp_mpm.sys_utils import net  DEFAULT_BRIDGE_MODE = False @@ -30,7 +30,9 @@ class XportMgrUDP:      iface_config = {}      bridges = {} -    def __init__(self, log, args): +    def __init__(self, log, args, eth_dispatcher_cls=None): +        self.eth_dispatcher_cls = eth_dispatcher_cls or \ +            importlib.import_module('usrp_mpm.ethdispatch').EthDispatcherCtrl          assert self.iface_config          assert all((              all((key in x for key in ('label',))) @@ -40,7 +42,7 @@ class XportMgrUDP:          self.log.trace("Initializing UDP xport manager...")          self._possible_chdr_ifaces = self.iface_config.keys()          self.log.trace("Identifying available network interfaces...") -        self.chdr_port = EthDispatcherCtrl.DEFAULT_VITA_PORT[0] +        self.chdr_port = self.eth_dispatcher_cls.DEFAULT_VITA_PORT[0]          self._chdr_ifaces = self._init_interfaces(self._possible_chdr_ifaces)          self._bridge_mode = args.get('bridge_mode', DEFAULT_BRIDGE_MODE)          self._eth_dispatchers = {} @@ -106,7 +108,7 @@ class XportMgrUDP:                  "Updated dispatchers in bridge mode with bridge interface {}"                  .format(bridge_iface))              self._eth_dispatchers = { -                x: EthDispatcherCtrl(self.iface_config[x]['label']) +                x: self.eth_dispatcher_cls(self.iface_config[x]['label'])                  for x in self.bridges[bridge_iface]              }              for dispatcher, table in iteritems(self._eth_dispatchers): @@ -132,7 +134,7 @@ class XportMgrUDP:                      continue                  if iface not in self._eth_dispatchers:                      self._eth_dispatchers[iface] = \ -                        EthDispatcherCtrl(self.iface_config[iface]['label']) +                        self.eth_dispatcher_cls(self.iface_config[iface]['label'])                  self._eth_dispatchers[iface].set_ipv4_addr(                      self._chdr_ifaces[iface]['ip_addr']                  ) @@ -218,7 +220,7 @@ class XportMgrUDP:          deinit() was not yet called.          """          assert host_location in ('remote', 'local', 'all') -           +          return [              {                  'ipv4': str(iface_info['ip_addr']) if (self.iface_config[iface_name]['type'] != 'internal') | 
