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
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
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()
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)))
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)))
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()
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)
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)
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
def get_file_handler(log_file_name): from miniworld.util import PathUtil return logging.FileHandler(PathUtil.get_log_file_path(log_file_name))
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):
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):
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 ""
def get_qemu_sock_path(node_id): return PathUtil.get_temp_file_path("qemu_monitor_%s.sock" % node_id)
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.