def decide_drop(self, dp_event): ''' Returns True if this event should be dropped, False otherwise ''' # dp_event is a DpPacketOut object # TODO(cs): should have a sanity check in here somewhere to make sure # we're still dropping the right number of packets. Test on a high drop # rate fuzzer_params dp_fingerprint = (DPFingerprint.from_pkt(dp_event.packet), dp_event.node.dpid, dp_event.port.port_no) # Skip over the class name (first element of the tuple) event_fingerprint = find(lambda f: f[1:] == dp_fingerprint, self.current_dp_fingerprints) if event_fingerprint is None: # Default to permit if we didn't expect this dp event return False # Flush this event from both our current window and our events list, so # that we don't accidentally conflate distinct dp_events with the same # fingerprint self.current_dp_fingerprints.remove(event_fingerprint) event_idx = self.fingerprint_2_event_idx[event_fingerprint] self.events.pop(event_idx) # First element of the tuple is the Event class name if event_fingerprint[0] == "DataplanePermit": return False self.stats.record_drop(event_fingerprint) return True # DataplaneDrop
def decide_drop(self, dp_event): ''' Returns True if this event should be dropped, False otherwise ''' # dp_event is a DpPacketOut object # TODO(cs): should have a sanity check in here somewhere to make sure # we're still dropping the right number of packets. Test on a high drop # rate fuzzer_params dp_fingerprint = (DPFingerprint.from_pkt(dp_event.packet), dp_event.node.dpid, dp_event.port.port_no) # Skip over the class name (first element of the tuple) event_fingerprint = find(lambda f: f[1:] == dp_fingerprint, self.current_dp_fingerprints) if event_fingerprint is None: # Default to permit if we didn't expect this dp event return False # Flush this event from both our current window and our events list, so # that we don't accidentally conflate distinct dp_events with the same # fingerprint self.current_dp_fingerprints.remove(event_fingerprint) event_idx = self.fingerprint_2_event_idx[event_fingerprint].pop() self.events.pop(event_idx) # First element of the tuple is the Event class name if event_fingerprint[0] == "DataplanePermit": return False self.stats.record_drop(event_fingerprint) return True # DataplaneDrop
def get_demuxer_for_server_info(self, server_info): ''' server_info is the address of the controller, either a tuple (address, port) or a path to a unix domain socket Raises a ValueError if there is no SocketDemultiplexer associated with the given server_info. ''' demuxer = find(lambda d: d.server_info == server_info, self.demuxers) if demuxer is None: return ValueError("server_info %s does not have a demuxer" % server_info) return demuxer
def migrate_host(self, hid, dpid): topology = self.simulation.topology host = topology.get_host(hid) # TODO(cs): make this lookup more efficient access_link = find(lambda a: a.host == host, topology.access_links) old_ingress_dpid = access_link.switch.dpid old_ingress_port_no = access_link.switch_port.port_no new_switch = topology.get_switch(dpid) new_port_no = max(new_switch.ports.keys()) + 1 self._log_input_event( HostMigration(old_ingress_dpid, old_ingress_port_no, dpid, new_port_no, access_link.host.hid) ) self.simulation.topology.migrate_host(old_ingress_dpid, old_ingress_port_no, dpid, new_port_no) self._send_initialization_packet(access_link.host, send_to_self=True)
def migrate_host(self, hid, dpid): topology = self.simulation.topology host = topology.get_host(hid) # TODO(cs): make this lookup more efficient access_link = find(lambda a: a.host == host, topology.access_links) old_ingress_dpid = access_link.switch.dpid old_ingress_port_no = access_link.switch_port.port_no new_switch = topology.get_switch(dpid) new_port_no = max(new_switch.ports.keys()) + 1 self.simulation.topology.migrate_host(old_ingress_dpid, old_ingress_port_no, dpid, new_port_no) self._log_input_event(HostMigration(old_ingress_dpid, old_ingress_port_no, dpid, new_port_no, access_link.host.hid)) self._send_initialization_packet(access_link.host, send_to_self=True)
def find_git_dir(results_dir): return find(lambda f: os.path.exists(os.path.join(f, ".git" )), walk_dirs_up(results_dir))
def peek(self, dag): ''' If dag.events == [], returns immediately. If dag.events != [], assumes that isinstance(dag.events[0], ConnectToControllers) ''' if dag.input_events == []: return dag # post: len(dag.input_events) > 0 unsupported_types = [ProcessFlowMod, FailFlowMod, DataplaneDrop] if find(lambda e: type(e) in unsupported_types, dag.events) is not None: raise ValueError('''Delayed flow_mods not yet supported. Please ''' '''implement the TODO near the sleep() call in play_forward()''') if not isinstance(dag.events[0], ConnectToControllers): raise ValueError("First event must be ConnectToControllers") simulation = None try: # Inferred events includes input events and internal events inferred_events = [] (simulation, controller) = self.setup_simulation() # dag.input_events returns only prunable input events. We also need # include ConnectToControllers, which is a non-prunable input event. assert(dag.input_events[0] != dag.events[0]) snapshot_inputs = [dag.events[0]] + dag.input_events # The input event from the previous iteration, followed by the internal # events that were inferred from the previous peek(). # Since we assume that the first event is always ConnectToControllers, # there are never internal events (nor input events) preceding the # initial ConnectToControllers. # TODO(cs): might not want to contrain the caller's dag in this way. events_inferred_last_iteration = [] for inject_input_idx in xrange(0, len(snapshot_inputs)): inject_input = get_inject_input(inject_input_idx, snapshot_inputs) following_input = get_following_input(inject_input_idx, snapshot_inputs) expected_internal_events = \ get_expected_internal_events(inject_input, following_input, dag.events) log.debug("peek()'ing after input %d (%s)" % (inject_input_idx, str(inject_input))) inferred_events += events_inferred_last_iteration if expected_internal_events == []: # Optimization: if no internal events occured between this input and the # next, no need to peek(), just replay the next input log.debug("Optimization: no expected internal events") Peeker.ambiguous_counts[0.0] += 1 events_inferred_last_iteration = [inject_input] continue # We replay events_inferred_last_iteration (internal events preceding # inject_input), as well as a NOPInput with the same timestamp as inject_input # to ensure that the timing is the same before peek()ing. fencepost = NOPInput(time=inject_input.time, round=inject_input.round) dag_interval = EventDag(events_inferred_last_iteration + [fencepost]) wait_time_seconds = self.get_wait_time_seconds(inject_input, following_input) found_events = self.find_internal_events(simulation, controller, dag_interval, inject_input, wait_time_seconds) events_inferred_last_iteration = [inject_input] events_inferred_last_iteration += match_and_filter(found_events, expected_internal_events) # Don't forget the final iteration's output inferred_events += events_inferred_last_iteration finally: if simulation is not None: simulation.clean_up() return EventDag(inferred_events)
def setup_experiment(args, config): # Grab parameters if args.exp_name: config.exp_name = args.exp_name elif not hasattr(config, 'exp_name'): config.exp_name = exp_lifecycle.guess_config_name(config) if not hasattr(config, 'results_dir'): config.results_dir = "experiments/%s" % config.exp_name if args.timestamp_results is not None: # Note that argparse returns a list config.timestamp_results = args.timestamp_results if hasattr(config, 'timestamp_results') and config.timestamp_results: now = timestamp_string() config.results_dir += "_" + str(now) # Set up results directory create_python_dir("./experiments") create_clean_python_dir(config.results_dir) # Copy stdout and stderr to a file "simulator.out" tee = Tee(open(os.path.join(config.results_dir, "simulator.out"), "w")) tee.tee_stdout() tee.tee_stderr() # Load log configuration. # N.B. this must be done after Tee()'ing. if args.log_config: logging.config.fileConfig(args.log_config) else: logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) # If specified, set up a config file for each controller for controller_config in config.simulation_config.controller_configs: if controller_config.config_template: controller_config.generate_config_file(config.results_dir) # Make sure that there are no uncommited changes if args.publish: exp_lifecycle.publish_prepare(config.exp_name, config.results_dir) # Record machine information for this experiment additional_metadata = None if hasattr(config, "get_additional_metadata"): additional_metadata = config.get_additional_metadata() exp_lifecycle.dump_metadata("%s/metadata" % config.results_dir, additional_metadata=additional_metadata) # Copy over config file config_file = re.sub(r'\.pyc$', '.py', config.__file__) if os.path.exists(config_file): canonical_config_file = config.results_dir + "/orig_config.py" if os.path.abspath(config_file) != os.path.abspath(canonical_config_file): shutil.copy(config_file, canonical_config_file) # Check configuration warnings log = logging.getLogger("setup") con = config.control_flow.simulation_cfg.controller_configs def builtin_pox_controller(c): # pox/ is already accounted for in metadata. return ("POXController" in str(c.controller_class) and c.cwd is not None and re.match("^pox[/]?", c.cwd) is not None) if (not hasattr(config, "get_additional_metadata") and find(lambda c: not builtin_pox_controller(c), config.control_flow.simulation_cfg.controller_configs) is not None): log.warn('''No get_additional_metadata() defined for config file. See ''' '''config/nox_routing.py for an example.''')
def peek(self, dag): ''' If dag.events == [], returns immediately. If dag.events != [], assumes that isinstance(dag.events[0], ConnectToControllers) ''' if dag.input_events == []: return dag # post: len(dag.input_events) > 0 unsupported_types = [ProcessFlowMod, DataplaneDrop] if find(lambda e: type(e) in unsupported_types, dag.events) is not None: raise ValueError('''Delayed flow_mods not yet supported. Please ''' '''implement the TODO near the sleep() call in play_forward()''') if not isinstance(dag.events[0], ConnectToControllers): raise ValueError("First event must be ConnectToControllers") simulation = None try: # Inferred events includes input events and internal events inferred_events = [] (simulation, controller) = self.setup_simulation() # dag.input_events returns only prunable input events. We also need # include ConnectToControllers, which is a non-prunable input event. assert(dag.input_events[0] != dag.events[0]) snapshot_inputs = [dag.events[0]] + dag.input_events # The input event from the previous iteration, followed by the internal # events that were inferred from the previous peek(). # Since we assume that the first event is always ConnectToControllers, # there are never internal events (nor input events) preceding the # initial ConnectToControllers. # TODO(cs): might not want to contrain the caller's dag in this way. events_inferred_last_iteration = [] for inject_input_idx in xrange(0, len(snapshot_inputs)): inject_input = get_inject_input(inject_input_idx, snapshot_inputs) following_input = get_following_input(inject_input_idx, snapshot_inputs) expected_internal_events = \ get_expected_internal_events(inject_input, following_input, dag.events) log.debug("peek()'ing after input %d (%s)" % (inject_input_idx, str(inject_input))) inferred_events += events_inferred_last_iteration # We replay events_inferred_last_iteration (internal events preceding # inject_input), as well as a NOPInput with the same timestamp as inject_input # to ensure that the timing is the same before peek()ing. fencepost = NOPInput(time=inject_input.time, round=inject_input.round) dag_interval = EventDag(events_inferred_last_iteration + [fencepost]) if expected_internal_events == []: # Optimization: if no internal events occured between this input and the # next, no need to peek(), just bring the simulation's state forward up to # inject_input log.debug("Optimization: no expected internal events") Peeker.ambiguous_counts[0.0] += 1 self.replay_interval(simulation, dag_interval, 0) events_inferred_last_iteration = [inject_input] continue wait_time_seconds = self.get_wait_time_seconds(inject_input, following_input) (found_events, snapshotter) = self.find_internal_events(simulation, controller, dag_interval, inject_input, wait_time_seconds) events_inferred_last_iteration = [inject_input] events_inferred_last_iteration += match_and_filter(found_events, expected_internal_events) snapshotter.snapshot_proceed() # Don't forget the final iteration's output inferred_events += events_inferred_last_iteration finally: if simulation is not None: simulation.clean_up() return EventDag(inferred_events)
def setup_experiment(args, config): # Grab parameters if args.exp_name: config.exp_name = args.exp_name elif not hasattr(config, 'exp_name'): config.exp_name = exp_lifecycle.guess_config_name(config) if not hasattr(config, 'results_dir'): config.results_dir = "experiments/%s" % config.exp_name if args.timestamp_results is not None: # Note that argparse returns a list config.timestamp_results = args.timestamp_results if hasattr(config, 'timestamp_results') and config.timestamp_results: now = timestamp_string() config.results_dir += "_" + str(now) # Set up results directory create_python_dir("./experiments") create_clean_python_dir(config.results_dir) # Copy stdout and stderr to a file "simulator.out" tee = Tee(open(os.path.join(config.results_dir, "simulator.out"), "w")) tee.tee_stdout() tee.tee_stderr() # Load log configuration. # N.B. this must be done after Tee()'ing. if args.log_config: logging.config.fileConfig(args.log_config) else: logging.basicConfig( level=logging.DEBUG if args.verbose else logging.INFO) # If specified, set up a config file for each controller for controller_config in config.simulation_config.controller_configs: if controller_config.config_template: controller_config.generate_config_file(config.results_dir) # Make sure that there are no uncommited changes if args.publish: exp_lifecycle.publish_prepare(config.exp_name, config.results_dir) # Record machine information for this experiment additional_metadata = None if hasattr(config, "get_additional_metadata"): additional_metadata = config.get_additional_metadata() exp_lifecycle.dump_metadata("%s/metadata" % config.results_dir, additional_metadata=additional_metadata) # Copy over config file config_file = re.sub(r'\.pyc$', '.py', config.__file__) if os.path.exists(config_file): canonical_config_file = config.results_dir + "/orig_config.py" if os.path.abspath(config_file) != os.path.abspath( canonical_config_file): shutil.copy(config_file, canonical_config_file) # Check configuration warnings log = logging.getLogger("setup") con = config.control_flow.simulation_cfg.controller_configs def builtin_pox_controller(c): # pox/ is already accounted for in metadata. return ("POXController" in str(c.controller_class) and c.cwd is not None and re.match("^pox[/]?", c.cwd) is not None) if (not hasattr(config, "get_additional_metadata") and find(lambda c: not builtin_pox_controller(c), config.control_flow.simulation_cfg.controller_configs) is not None): log.warn( '''No get_additional_metadata() defined for config file. See ''' '''config/nox_routing.py for an example.''')