예제 #1
0
def logger():
    global _logger
    if _logger is None:
        _logger = get_logger("WireFilter", handlers=[])
        _logger.addHandler(
            logging.FileHandler(PathUtil.get_log_file_path("wirefilter.txt")))
    return _logger
예제 #2
0
def logger():
    global _logger
    if _logger is None:
        _logger = get_logger("Spatial Backend")
        _logger.addHandler(
            logging.FileHandler(
                PathUtil.get_log_file_path("spatial_backend.txt")))
    return _logger
예제 #3
0
 def start(self):
     log.info("starting log writer (file: '%s')", self.log_filename)
     if self.fh_logfile is None:
         self.fh_logfile = open(
             PathUtil.get_log_file_path(self.log_filename), "w")
     self.writer_thread = ExceptionStopThread.run_fun_threaded_n_log_exception(
         target=self.check, tkwargs=dict(name="LogWriter"))
     self.writer_thread.daemon = True
     self.writer_thread.start()
예제 #4
0
 def get_wirefilter_uds_socket_path(node_a_id, node_b_id, interface_a,
                                    interface_b):
     return PathUtil.get_temp_file_path(
         "wirefilter_%s_%s.sock" %
         (InterfaceDependentID.get_interface_class_dependent_id(
             node_a_id, interface_a.node_class,
             interface_a.nr_host_interface),
          InterfaceDependentID.get_interface_class_dependent_id(
              node_b_id, interface_b.node_class,
              interface_b.nr_host_interface)))
예제 #5
0
    def ip_config(self):
        # NOTE: we need to reuse the existing configurators due to their internal state!
        if scenario_config.is_network_links_auto_ipv4():
            log.info("using ip provisioner: %s" % scenario_config.get_network_provisioner_name())

            new_connections = self.get_new_connections_with_interfaces_since_last_distance_matrix_change()
            with open(PathUtil.get_log_file_path("%s.txt" % self.__class__.__name__), "a") as f:

                if self.net_configurator.needs_reconfiguration(singletons.simulation_manager.current_step):
                    # only connection setup and check for new connections
                    log.info("%s: configuring network ...", self.net_configurator.__class__.__name__)
                    commands_per_node = self.net_configurator.get_nic_configuration_commands(new_connections)
                    self.net_configurator.apply_nic_configuration_commands(commands_per_node)

                    f.write("setup_commands: %s\n" % pformat(dict(commands_per_node)))
예제 #6
0
def init(config_path: str=None, do_init_singletons=True):
    """ Init the module by installing signal handlers and creating the temp files """

    if not os.path.exists(PATH_GLOBAL_CONFIG):
        # TODO: render config.json from config sleeve
        log.info('creating config ...')
        shutil.copy2('sample_configs/config.json', '.')

    # TODO: Ticket #2
    install_signal_handlers()
    set_global_config(config_path or PATH_GLOBAL_CONFIG)
    set_log_level(config.get_log_level())

    clean_miniworld_dir()
    log.addHandler(logging.FileHandler(PathUtil.get_log_file_path("stdout.txt")))

    if do_init_singletons:
        init_singletons()
예제 #7
0
    def __init__(self, emulation_node_x, emulation_node_y, interface_x,
                 interface_y):
        AbstractConnection.__init__(self, emulation_node_x, emulation_node_y,
                                    interface_x, interface_y)

        ################################
        # REPLable
        ################################

        REPLable.__init__(self)

        # unix domain socket paths
        self.path_uds_socket = self.get_wirefilter_uds_socket_path(
            self.emulation_node_x.id, self.emulation_node_y.id,
            self.interface_x, self.interface_y)
        # log file for qemu shell commands
        self.log_path_commands = PathUtil.get_log_file_path(
            "wirefilter_commands_%s.txt" % self.id)
예제 #8
0
    def __init__(self, id, emulation_node):
        VirtualizationLayer.__init__(self, id, emulation_node)

        self.emulation_node = emulation_node

        # log file for qemu boot
        self.log_path_qemu_boot = PathUtil.get_log_file_path(
            "qemu_boot_%s.txt" % self.id)

        self.monitor_repl = QemuMonitorRepl(self)

        self.booted_from_snapshot = False

        ################################
        # REPLable
        ################################

        REPLable.__init__(self)

        # unix domain socket paths
        self.path_uds_socket = self.get_qemu_sock_path(self.id)
예제 #9
0
    def create_qemu_overlay_image(self, base_image_path):
        """
        Create an overlay image used for write operations (based on the image `base_image_path`)

        Returns the path of the created overlay image.
        None if an error occurred.

        Parameters
        ----------
        base_image_path: str
            Absolute path to image!

        Returns
        -------
        str
        """

        base_image_file_name = basename(base_image_path)

        # image name without file suffix
        overlay_image_name = splitext(base_image_file_name)[0]
        overlay_image_name = '%s_overlay_%s.img' % (overlay_image_name,
                                                    self.id)
        # get the temp file for the final overlay image
        overlay_image_path = PathUtil.get_temp_file_path(overlay_image_name)
        # create it
        cmd_qemu_create_overlay_image = CMD_TEMPLATE_QEMU_CREATE_OVERLAY_IMAGE.format(
            base_image_path=base_image_path,
            overlay_image_path=overlay_image_path)

        # TODO: #2 : error handling
        singletons.shell_helper.run_shell(
            self.id, cmd_qemu_create_overlay_image,
            [self.shell_prefix, "create_overlay",
             basename(base_image_path)])

        return overlay_image_path
예제 #10
0
def get_file_handler(log_file_name):
    from miniworld.util import PathUtil
    return logging.FileHandler(PathUtil.get_log_file_path(log_file_name))
예제 #11
0
from dictdiffer import DictDiffer
from miniworld.model.network.linkqualitymodels import LinkQualityConstants

from miniworld import singletons
from miniworld.Scenario import scenario_config
from miniworld.log import log
from miniworld.management.network.manager.provisioner.NetworkConfiguratorP2P import NetworkConfiguratorP2P
from miniworld.management.network.manager.provisioner.NetworkConfiguratorSameSubnet import NetworkConfiguratorSameSubnet
from miniworld.model.emulation.nodes.virtual.ManagementNode import ManagementNode
from miniworld.model.events.MyEventSystem import MyEventSystem
from miniworld.model.network.backends import NetworkBackendNotifications
from miniworld.model.network.connections.ConnectionStore import ConnectionStore
from miniworld.model.singletons.Resetable import Resetable
from miniworld.util import PathUtil

PATH_LOG_FILE_NETWORK_CHECK_COMMANDS = PathUtil.get_log_file_path("network_check_commands.txt")

NETWORK_CONFIGURATOR_NAME_SAME_SUBNET = 'same_subnet'
NETWORK_CONFIGURATOR_NAME_P2P = 'p2p'

__author__ = 'Nils Schmidt'

KEY_DISTANCE = "distance"


# TODO: MOVE CONNECTIONS HERE ...
# TODO: STORE EMUNODES OR NODE IDS?
# TODO: move link_quality stuff to connections


class NetworkManager(Resetable, NetworkBackendNotifications.NetworkBackendNotifications):
예제 #12
0
            return idx


GROUP_TUNNELS = find_empty_group()
# we cannot simply call find_empty_group with the same parameters because the method works only if a new group has been created!
# therefore the pass the start parameter!
GROUP_BRIDGES = find_empty_group(start=GROUP_TUNNELS + 1)
GROUP_TAP_DEVS = find_empty_group(start=GROUP_BRIDGES + 1)

GROUPS = {
    "tunnels": GROUP_TUNNELS,
    "bridges": GROUP_BRIDGES,
    "tap_devices": GROUP_TAP_DEVS
}

GROUPS_LOG_FILE = PathUtil.get_temp_file_path("iproute2_groups")

# log iproute2 groups
with open(GROUPS_LOG_FILE, "w") as f:
    log.info("writing the iproute2 groups to '%s'", GROUPS_LOG_FILE)
    f.write('\n'.join(["%s:%s" % (key, val) for key, val in GROUPS.items()]))

for iproute2_group, val in GROUPS.items():
    log.info("%s group is: %d" % (iproute2_group, val))


def get_bridge_add_cmd(bridge_dev_name):
    return "ip link add name {} type bridge".format(bridge_dev_name)


def get_bridge_set_hub_mode_cmd(bridge_dev_name):
예제 #13
0
    class ConnectionEbtables(ConnectionDummy()):
        """
        Ebtables setup example
        ----------------------
        # flush rules
        ebtables -F
        # set default policy
        ebtables -P FORWARD DROP

        # add new chain
        ebtables -N miniworld_tap -P ACCEPT
        # redirect to bridge chain
        ebtables -A FORWARD --logical-in miniworld_tap  -j br1
        # allow traffic between the two peers/interfaces
        ebtables (-t filter) -I FORWARD -i tap_00001_2 -o tap_00002_2 -j ACCEPT
        ebtables -I FORWARD -i tap_00002_2 -o tap_00001_2 -j ACCEPT

        Atomic version
        --------------
        ebtables --atomic-file ebtables_commit --atomic-save
        export EBTABLES_ATOMIC_FILE=ebtables_commit
        ebtables -I FORWARD -i tap_00003_2 -o tap_00002_2 -j ACCEPT
        ebtables -I FORWARD -i tap_00002_2 -o tap_00003_2 -j ACCEPT
        ebtables --atomic-commit
        unset EBTABLES_ATOMIC_FILE

        Delete Entries
        --------------
        ebtables -D FORWARD -i tap_00002_2 -o tap_00003_2 -j ACCEPT

        """

        policy_accept = "ACCEPT"
        policy_drop = "DROP"
        ebtables_cmd = "ebtables --concurrent"

        atomic_file = PathUtil.get_temp_file_path("ebtables_atommic")
        atomic_file_str = "--atomic-file %s" % atomic_file

        ebtable_cmd_atomic_init = "{ebtables} {atomic_file} --atomic-init".format(
            ebtables=ebtables_cmd, atomic_file=atomic_file_str)

        ebtable_cmd_atomic_save = "{ebtables} {atomic_file} --atomic-save".format(
            ebtables=ebtables_cmd, atomic_file=atomic_file_str)

        ebtable_cmd_atomic_commit = "{ebtables} {atomic_file} --atomic-commit".format(
            ebtables=ebtables_cmd, atomic_file=atomic_file_str)

        path_connection_log = PathUtil.get_log_file_path(
            "ebtable_connections.txt")

        # TODO: DOC
        _cnt_connections = 1

        @staticmethod
        def inc_counter():
            ConnectionEbtables._cnt_connections += 1
            return ConnectionEbtables._cnt_connections - 1

        @staticmethod
        def get_connection_id(tap_x, tap_y):
            key = tuple(sorted([tap_x, tap_y]))
            return ConnectionEbtables.connections[key]

        # TODO: we should use hosts rather than interfaces here!
        # dict<(str, str>, int>
        # for each connection a connection identifier
        connections = defaultdict(lambda: ConnectionEbtables.inc_counter())

        #########################################
        # Overwrite connection handling methods
        #########################################

        # TODO: #84:
        def tap_link_up(self, tap_x, tap_y, up=True):
            chain = singletons.network_backend.get_br_name(
                self.interface_x.nr_host_interface)
            change_cmd = self._get_ebtables_cmd(chain, tap_x, tap_y, up)

            network_backend = singletons.network_backend
            # add to command queue
            network_backend.add_shell_ebtables_command(
                network_backend.EVENT_EBTABLES_COMMANDS, change_cmd)

        def tap_link_up_central(self, tap_x, tap_y, up=True):
            log.info("accept all packets in FORWARD chain ...")
            self.run_shell("{ebtables} -P FORWARD ACCEPT".format(
                ebtables=ConnectionEbtables.ebtables_cmd))

        def tap_link_up_remote(self, tap_x, tap_y, up=True):
            self.tap_link_up(tap_x, tap_y, up=up)

        #########################################
        ###
        #########################################

        # TODO: clear command list for every step!
        @staticmethod
        def reset_ebtable_commands():
            ConnectionEbtables.ebtable_commands = []

        @staticmethod
        def run_shell(cmd):
            return singletons.shell_helper.run_shell(
                ConnectionEbtables.__class__.__name__,
                cmd,
                prefixes=["ebtables"])

        @staticmethod
        def set_ebtables_forward_policy_accept():
            ConnectionEbtables.set_ebtables_forward_policy(
                ConnectionEbtables.policy_accept)

        @staticmethod
        def set_ebtables_forward_policy_drop():
            ConnectionEbtables.set_ebtables_forward_policy(
                ConnectionEbtables.policy_drop)

        @staticmethod
        def set_ebtables_forward_policy(policy):
            log.info("{policy} all packets in FORWARD chain ...".format(
                policy=policy))
            ConnectionEbtables.run_shell(
                "{ebtables} -P FORWARD {policy}".format(
                    ebtables=ConnectionEbtables.ebtables_cmd, policy=policy))

        def _get_ebtables_cmd(self, chain, tap_x, tap_y, up):
            # insert or delete?
            up_str = "-I" if up else "-D"
            # use ebtables atomic mode only in batch mode

            # TODO: DOC

            connection_id = ConnectionEbtables.get_connection_id(tap_x, tap_y)
            mark_str = "mark --set-mark {id} --mark-target {policy}".format(
                id=connection_id, policy=self.policy_accept)

            with open(self.path_connection_log, "a") as f:
                f.write("%s,%s: %d\n" % (tap_x, tap_y, connection_id))

            return "{ebtables} {atomic_prefix} {up_str} {chain} -i {tap_x} -o {tap_y} -j {policy}".format(
                ebtables=self.ebtables_cmd,
                atomic_prefix=self.get_ebtables_atomix_prefix(),
                up_str=up_str,
                chain=chain,
                tap_x=tap_x,
                tap_y=tap_y,
                policy=mark_str)

        @staticmethod
        def get_ebtables_atomix_prefix():
            return "--atomic-file %s" % ConnectionEbtables.atomic_file if scenario_config.is_network_backend_bridged_execution_mode_batch(
            ) else ""

        @staticmethod
        def get_ebtables_chain_cmd(name, policy):
            return "{ebtables} {atomix_prefix} -N {chain} -P {policy}".format(
                ebtables=ConnectionEbtables.ebtables_cmd,
                atomix_prefix=ConnectionEbtables.get_ebtables_atomix_prefix(),
                chain=name,
                policy=policy)

        @staticmethod
        def get_ebtables_redirect_cmd(br_name):
            return "{ebtables} {atomic_prefix} -A FORWARD --logical-in {br_name} -j {br_name}".format(
                ebtables=ConnectionEbtables.ebtables_cmd,
                atomic_prefix=ConnectionEbtables.get_ebtables_atomix_prefix(),
                br_name=br_name)

        @staticmethod
        def get_ebtables_clear_cmd():
            return "{ebtables} -F".format(
                ebtables=ConnectionEbtables.ebtables_cmd)

        @staticmethod
        def get_ebtables_delete_chain_cmd(chain):
            return "{ebtables} -X {chain}".format(
                ebtables=ConnectionEbtables.ebtables_cmd, chain=chain)

        def _add_filter_cmd(self, dev_name, connection_id):
            # TODO: DOC
            filter_cmd = "tc filter replace dev {tap_dev_name} parent 1:0 protocol all handle {id} fw flowid 1:{id}".format(
                tap_dev_name=dev_name, id=connection_id)

            self.add_shell_command(self.EVENT_LINK_SHAPE_ADD_FILTER,
                                   filter_cmd)

        def _get_default_class(self):
            # use filter instead
            return ""
예제 #14
0
 def get_qemu_sock_path(node_id):
     return PathUtil.get_temp_file_path("qemu_monitor_%s.sock" % node_id)
예제 #15
0
                        newline_idx = self.descriptor_buffers[fd].index("\n")
                    # ValueError: substring not found
                    # for "\n"
                    except ValueError:
                        continue

                    data = self.descriptor_buffers[fd][:newline_idx]

                    self.fh_logfile.write('%s: %s\n' % (prefix, data))
                    self.fh_logfile.flush()

                    self.descriptor_buffers[fd] = self.descriptor_buffers[
                        fd][:newline_idx]


FOREGROUND_SHELL_LOG_PATH = PathUtil.get_log_file_path(
    "foreground_shell_commands.txt")


class ShellHelper(Resetable):
    """
    Provides help for starting shell process in fore- and background.
     For the background threads, the stdout/stderr file descriptors are read
     and further process by the :py:class:`.LogWriter` thread.

     All started background processes are stored in :py:attr:`subprocesses`.
     Furthermore all bg processes are monitored for their return codes.

    Note that creating an instance of this class creates two threads: py:class:`.LogWriter` and the bg process checker.
    Therefore, one should use the singleton object!

    Attributes
    class NetworkBackendBridgedSingleDevice(NetworkBackendBridgedDummy()):
        """

        Do not set default FORWARD policy to DROP, because ebtables should have no effect on the management network.
        All other connections are redirected to another chain.

        Attributes
        ----------
        bridges : dict<str, AbstractSwitch>
            Stores for each bridge name the according class.
        """

        ebtables_history_path = PathUtil.get_log_file_path(
            "ebtables_history.txt")

        PREFIXES = ["ebtables"]

        # ShellCommandSerializer stuff
        # NOTE: due to same namespace, we must take core of not overriding the super event names as they are still used
        EBTABLES_EVENT_ROOT = "ebtables"
        EVENT_EBTABLES_CREATE_CHAINS = "ebtables_create_chain"
        EVENT_EBTABLES_REDIRECT = "ebtables_redirect"
        EVENT_EBTABLES_INIT = "ebtables_init"
        EVENT_EBTABLES_COMMANDS = "ebtables_commands"
        EVENT_EBTABLES_FINISH = "ebtables_finish"
        EBTABLES_EVENT_ORDER = OrderedSet([
            EVENT_EBTABLES_INIT, EVENT_EBTABLES_CREATE_CHAINS,
            EVENT_EBTABLES_REDIRECT, EVENT_EBTABLES_COMMANDS,
            EVENT_EBTABLES_FINISH
        ])

        # TODO: DOC
        mark_cnt = 0

        def get_interface_filter(self):
            return InterfaceFilter.EqualInterfaceNumbers

        def get_network_provisioner(self):
            return NetworkConfiguratorSameSubnet

        @staticmethod
        def add_shell_ebtables_command(event, cmd):
            singletons.network_backend.shell_command_executor.add_command(
                NetworkBackendBridgedSingleDevice.EBTABLES_EVENT_ROOT, event,
                NetworkBackendBridgedSingleDevice.__class__.__name__, cmd,
                NetworkBackendBridgedSingleDevice.PREFIXES)

        def setup_shell_command_executor(self, shell_command_executor):

            shell_command_executor.add_group(self.EBTABLES_EVENT_ROOT)
            shell_command_executor.set_event_order(self.EBTABLES_EVENT_ROOT,
                                                   self.EBTABLES_EVENT_ORDER)

        def __init__(self, *args, **kwargs):
            super(NetworkBackendBridgedSingleDevice,
                  self).__init__(*args, **kwargs)
            self.bridges = {}

        def run_shell(self, cmd):
            return singletons.shell_helper.run_shell("network_backend",
                                                     cmd,
                                                     prefixes=["ebtables"])

        def reset(self):
            super(NetworkBackendBridgedSingleDevice, self).reset()

            self.init_ebtables()

        def before_simulation_step(self, simulation_manager, step_cnt,
                                   network_backend, emulation_nodes, **kwargs):
            """
            Add atomic commit command for ebtables network change.

            Parameters
            ----------
            simulation_manager
            step_cnt
            network_backend
            emulation_nodes
            """

            conn_type = self.network_backend_bootstrapper.connection_type
            # TODO: MOVE method here ...
            if scenario_config.is_network_backend_bridged_execution_mode_batch(
            ):
                self.add_shell_ebtables_command(
                    self.EVENT_EBTABLES_INIT,
                    conn_type.ebtable_cmd_atomic_save)

            if step_cnt == 0:
                self.init_ebtables()

        def init_ebtables(self):
            conn_type = self.network_backend_bootstrapper.connection_type

            # reset to initial ebtables state
            self.run_shell(conn_type.ebtable_cmd_atomic_init)
            self.run_shell(conn_type.ebtable_cmd_atomic_commit)

        def get_br_name(self, number):
            return 'wifi%d' % number

        def get_bridge(self, interface):
            return self.bridges[self.get_br_name(interface.nr_host_interface)]

        def before_link_initial_start(self,
                                      network_backend,
                                      emulation_node_x,
                                      emulation_node_y,
                                      interface_x,
                                      interface_y,
                                      connection_info,
                                      start_activated=False,
                                      **kwargs):

            super(NetworkBackendBridgedSingleDevice,
                  self).before_link_initial_start(network_backend,
                                                  emulation_node_x,
                                                  emulation_node_y,
                                                  interface_x,
                                                  interface_y,
                                                  connection_info,
                                                  start_activated=staticmethod,
                                                  **kwargs)
            # start a single bridge here and add all tap devices to it
            # afterwards use ebtables for connection filtering on layer 2
            connection = None

            connection_type = self.network_backend_bootstrapper.connection_type

            # TODO: DOC
            br_name = self.get_br_name(interface_x.nr_host_interface)
            bridge = None
            if not self.bridges.get(br_name, None):
                log.info("creating bridge %s", br_name)
                bridge = self.bridges[
                    br_name] = self.network_backend_bootstrapper.switch_type(
                        br_name, interface_x)
                # create extra chain for bridge
                self.add_shell_ebtables_command(
                    self.EVENT_EBTABLES_CREATE_CHAINS,
                    connection_type.get_ebtables_chain_cmd(
                        br_name, connection_type.policy_drop))
                # redirect to new chain
                self.add_shell_ebtables_command(
                    self.EVENT_EBTABLES_REDIRECT,
                    connection_type.get_ebtables_redirect_cmd(br_name))

                bridge.start(switch=False, bridge_dev_name=br_name)
            else:
                bridge = self.get_bridge(interface_x)

            connection = connection_type(emulation_node_x, emulation_node_y,
                                         interface_x, interface_y,
                                         connection_info)
            connection.start(self)

            # TODO: #84: improve
            connections = Connections([(emulation_node_x, interface_x),
                                       (emulation_node_y, interface_y)])

            is_one_tap_mode = connection_info.is_central or connection_info.is_mgmt or connection_info.is_remote_conn
            if not is_one_tap_mode:
                tap_x = self.get_tap_name(emulation_node_x.id, interface_x)
                tap_y = self.get_tap_name(emulation_node_y.id, interface_y)

                # TODO: #84: check which devices are already added to the bridge ...
                # add devices to bridge
                bridge.add_if(tap_x, if_up=True)
                bridge.add_if(tap_y, if_up=True)

            # TODO: nearly same code as in NetworkBackendBridgedMultiDevice!
            else:
                virtual_node, _if = None, None
                if connection_info.is_central:
                    virtual_node, _if = connections.filter_central_nodes()[0]
                elif connection_info.is_mgmt:
                    virtual_node, _if = connections.filter_mgmt_nodes()[0]

                tap_dev_name = None
                if connection_info.is_remote_conn:

                    tunnel_dev_name = self.get_tunnel_name(
                        emulation_node_x.id, emulation_node_y.id)
                    # add the tunnel to the bridge
                    bridge.add_if(tunnel_dev_name, if_up=True)
                    remote_node, if_remote_node, local_emu_node, if_local_emu_node = singletons.simulation_manager.get_remote_node(
                        emulation_node_x, emulation_node_y, interface_x,
                        interface_y)
                    # the tap device we want to add to the bridge is the local one, not the remote one!
                    tap_dev_name = self.get_tap_name(local_emu_node.id,
                                                     if_local_emu_node)
                else:
                    emu_node, emu_if = connections.filter_real_emulation_nodes(
                    )[0]
                    tap_dev_name = self.get_tap_name(emu_node.id, emu_if)
                    bridge = virtual_node.switch

                # add the tap device to the bridge
                bridge.add_if(tap_dev_name, if_up=True)

            return True, bridge, connection

        def do_network_topology_change(self):
            """
            Run ebtable commands for batch or one shell call mode
            """
            is_pyroute2 = scenario_config.is_network_backend_bridged_execution_mode_pyroute2(
            )
            is_batch = scenario_config.is_network_backend_bridged_execution_mode_batch(
            )
            if is_batch:
                # add atomic commit command for ebtables network change
                conn_type = self.network_backend_bootstrapper.connection_type
                self.add_shell_ebtables_command(
                    self.EVENT_EBTABLES_FINISH,
                    conn_type.ebtable_cmd_atomic_commit)

                # ebtables_cmds = self.shell_command_executor.get_serialized_commands_event_order(self.EBTABLES_EVENT_ROOT)
                #
                # if scenario_config.is_network_backend_bridged_execution_mode_one_shell_call():
                #     # variant #1
                #     # TODO: cnt =1
                #     # TODO: DOCU
                #     # cmd = "sh -c '%s'" % ';\n'.join(ebtables_cmds)
                #     # # TODO: place node_id other than 0!
                #     # self.run_shell(cmd)
                #
                #     # variant #2: os.system
                #     # import os
                #     # for cmd in ebtables_cmds:
                #     #     os.system(cmd)
                #
                #     # variant #3: subprocess.check_output
                #     from miniworld.management import ShellHelper as sh
                #     for cmd in ebtables_cmds:
                #         sh.run_shell(cmd)
                #

                if scenario_config.is_network_backend_bridged_execution_mode_batch(
                ):
                    self.shell_command_executor.run_commands(
                        events_order=self.EBTABLES_EVENT_ROOT)

            # may be already executed by batch
            if not is_batch and is_pyroute2:
                self.shell_command_executor.run_commands(
                    events_order=self.EBTABLES_EVENT_ROOT)
    from miniworld.model.network.backends.bridged.multidevice.NetworkBackendBridgedMultiDevice import \
        NetworkBackendBridgedMultiDevice
    from miniworld.model.network.backends.bridged.singledevice.NetworkBackendBridgedSingleDevice import \
        NetworkBackendBridgedSingleDevice

    return NetworkBackendBridgedSingleDevice() if scenario_config.is_network_backend_bridged_connection_mode_single() else NetworkBackendBridgedMultiDevice()


# NOTE: note matching against "'" and ";" is done for a execution mode (see :py:meth.`Scenario.is_network_backend_bridged_execution_mode_one_shell_call`)
re_find_ip = re.compile("^ip\s+(.*)", re.MULTILINE)

# regex to strip tc prefix
re_tc = re.compile("^tc\s+(.*)", re.MULTILINE)

# regex to strip ip prefix
PATH_SHELL_COMMANDS = PathUtil.get_log_file_path("network_backend_%s.txt" % "shell_commands")


def NetworkBackendBridgedDummy():
    class NetworkBackendBridgedDummy(NetworkBackend.NetworkBackend()):

        """
        Implementation of a network backend which uses Linux Ethernet Bridiging.

        The backend supports 3 execution modes:
        1. iproute2
        2. pyroute
        3. brctl

        Each execution mode is implemented as a subclass.