def _setUpClass(cls): for prefix_name in cls.DATA: prefix = cls.DATA[prefix_name] net = IPNetwork(prefix) if "/" in prefix else IPAddress(prefix) if str(net) != prefix: raise ValueError( "Prefix '{}' is not represented in its canonical form: " "'{}' used, '{}' expected.".format(prefix_name, prefix, str(net))) cls.info("{}: setting instances up...".format(cls.SHORT_DESCR)) MockedEnv(cls, base_dir=cls._get_module_dir(), peering_db=cls.MOCK_PEERING_DB, ripe_rpki_cache=cls.MOCK_RIPE_RPKI_CACHE, irrdb=cls.MOCK_IRRDB, rttgetter=cls.MOCK_RTTGETTER, arin_db_dump=cls.MOCK_ARIN_DB_DUMP) cls.rs_cfg_file_path = None try: cls._setup_instances() except: cls.tearDownClass() raise if "BUILD_ONLY" in os.environ or \ (cls.SKIP_ON_TRAVIS and "TRAVIS" in os.environ): cls.debug("Skipping starting instances") return try: for instance in cls.INSTANCES: instance.set_var_dir("{}/var".format(cls._get_module_dir())) if cls._do_not_stop_instances() and instance.is_running(): cls.debug( "Instance '{}' already running, reloading config". format(instance.name)) if not instance.reload_config(): raise InstanceError( "An error occurred while reloading '{}' configuration." .format(instance.name)) continue cls.debug("Starting instance '{}'...".format(instance.name)) instance.start() except Exception as e: try: cls.tearDownClass() except: pass raise e
def receive_route(self, inst, prefix, other_inst=None, as_path=None, next_hop=None, std_comms=None, lrg_comms=None, ext_comms=None, local_pref=None, filtered=None, only_best=None, reject_reason=None): """Test if the BGP speaker receives the expected route(s). If no routes matching the given criteria are found, the ``TestCase.fail()`` method is called and the test fails. Args: inst: the :class:`BGPSpeakerInstance` instance where the routes are searched on. prefix (str): the IPv4/IPv6 prefix of the routes to search for. other_inst: if given, only routes received from this :class:`BGPSpeakerInstance` instance are considered. as_path (str): if given, only routes with this AS_PATH are considered. next_hop: can be a string or a :class:`BGPSpeakerInstance` instance; if given, only routes that have a NEXT_HOP address matching this one are considered. std_comms, lrg_comms, ext_comms (list): if given, only routes that carry these BGP communities are considered. Use an empty list ([]) to consider only routes with no BGP comms. local_pref (int): if given, only routes with local-pref equal to this value are considered. filtered (bool): if given, only routes that have been (not) filtered are considered. only_best (bool): if given, only best routes are considered. reject_reason (int): valid only if `filtered` is True: if given the route must be reject with this reason code. It can be also a set of codes: in this case, the route must be rejected with one of those codes. The list of valid codes is reported in docs/CONFIG.rst or at https://arouteserver.readthedocs.io/en/latest/CONFIG.html#reject-policy """ assert isinstance(inst, BGPSpeakerInstance), \ "inst must be of class BGPSpeakerInstance" try: IPNetwork(prefix) except: raise AssertionError("prefix must be a valid IPv4/IPv6 prefix") if other_inst: assert isinstance(other_inst, BGPSpeakerInstance), \ "other_inst must be of class BGPSpeakerInstance" if as_path: try: if not isinstance(as_path, str): raise AssertionError() for asn in as_path.split(" "): assert asn.strip().isdigit() except: raise AssertionError("as_path must be a string in the format " "'<asn1> <asn2>' with <asnX> positive " "integers") next_hop_ip = None if next_hop: assert isinstance(next_hop, (str, BGPSpeakerInstance)), \ ("next_hop must be a string representing one IP address or " "a BGPSpeakerInstance object") next_hop_ip = next_hop if isinstance(next_hop, str) else next_hop.ip try: IPAddress(next_hop_ip) except: raise AssertionError( "Invalid next_hop IP address: {}".format(next_hop_ip)) if std_comms: assert isinstance(std_comms, list), \ ("std_comms must be a list of strings representing " "BGP standard communities") if ext_comms: assert isinstance(ext_comms, list), \ ("ext_comms must be a list of strings representing " "BGP extended communities") if lrg_comms: assert isinstance(lrg_comms, list), \ ("lrg_comms must be a list of strings representing " "BGP large communities") if local_pref: assert isinstance(local_pref, int), \ "local_pref must be an integer >= 0" assert local_pref >= 0, \ "local_pref must be an integer >= 0" if reject_reason is not None and not filtered: raise AssertionError( "reject_reason can be set only if filtered is True") reject_reasons = None if reject_reason is not None: if isinstance(reject_reason, int): reject_reasons = [reject_reason] else: reject_reasons = list(reject_reason) for code in reject_reasons: assert code in range(1, 15), "invalid reject_reason" include_filtered = filtered if filtered is not None else False best_only = only_best if only_best is not None else False routes = inst.get_routes(prefix, include_filtered=include_filtered, only_best=best_only) self.process_reject_cause_routes(routes) errors = [] if not routes: errors.append("{inst} does not receive {prefix} at all.") else: for route in routes: err = False if other_inst and route.via != other_inst.ip: errors.append( "{{inst}} receives {{prefix}} from {via} and not from {{other_inst}}." .format(via=route.via)) err = True if as_path and route.as_path != as_path: errors.append( "{{inst}} receives {{prefix}} with AS_PATH {as_path} and not with {{as_path}}." .format(as_path=route.as_path)) err = True if next_hop_ip and route.next_hop != next_hop_ip: errors.append( "{{inst}} receives {{prefix}} with NEXT_HOP {next_hop} and not with {{next_hop_ip}}." .format(next_hop=route.next_hop)) err = True if std_comms is not None and sorted( route.std_comms) != sorted(std_comms): errors.append( "{{inst}} receives {{prefix}} with std comms {comms} and not with {{std_comms}}." .format(comms=route.std_comms)) err = True if lrg_comms is not None and sorted( route.lrg_comms) != sorted(lrg_comms): errors.append( "{{inst}} receives {{prefix}} with lrg comms {comms} and not with {{lrg_comms}}." .format(comms=route.lrg_comms)) err = True if ext_comms is not None and sorted( route.ext_comms) != sorted(ext_comms): errors.append( "{{inst}} receives {{prefix}} with ext comms {comms} and not with {{ext_comms}}." .format(comms=route.ext_comms)) err = True if local_pref is not None and route.localpref != local_pref: errors.append( "{{inst}} receives {{prefix}} with local-pref {local_pref} and not with {{local_pref}}." .format(local_pref=route.localpref)) err = True if filtered is not None and route.filtered != filtered: errors.append( "{{inst}} receives {{prefix}} from {via}, AS_PATH {as_path}, NEXT_HOP {next_hop} " "but it is {filtered_status} while it is expected to be {filtered_exp}." .format(via=route.via, as_path=route.as_path, next_hop=route.next_hop, filtered_status="filtered" if route.filtered else "not filtered", filtered_exp="filtered" if filtered else "not filtered")) err = True if filtered is True and route.filtered and \ reject_reasons is not None and len(route.reject_reasons) > 0: reject_reason_found = False for real_reason in route.reject_reasons: if real_reason in reject_reasons: reject_reason_found = True if not reject_reason_found: if len(reject_reasons) == 1: exp_reason = reject_reasons[0] else: exp_reason = "one of {}".format(", ".join( map(str, reject_reasons))) errors.append( "{{inst}} receives {{prefix}} from {via}, AS_PATH {as_path}, NEXT_HOP {next_hop}, " "it is filtered but reject reasons don't match: real reasons {reason}, " "expected reason {exp_reason}.".format( via=route.via, as_path=route.as_path, next_hop=route.next_hop, reason=", ".join(map(str, route.reject_reasons)), exp_reason=exp_reason)) err = True if not err: return route if errors: criteria = [] if other_inst: criteria.append("from {} ({})".format(other_inst.ip, other_inst.name)) if as_path: criteria.append("with AS_PATH {}".format(as_path)) if next_hop_ip: criteria.append("with next-hop {}".format(next_hop_ip)) if std_comms: criteria.append("with std comms {}".format(std_comms)) if lrg_comms: criteria.append("with lrg comms {}".format(lrg_comms)) if ext_comms: criteria.append("with ext comms {}".format(ext_comms)) if local_pref: criteria.append("with local-pref {}".format(local_pref)) if filtered is True: criteria.append("filtered") if reject_reasons: if len(reject_reasons) == 1: criteria.append("with reject reason {}".format( reject_reasons[0])) else: criteria.append("with reject reason in {}".format( ", ".join(map(str, reject_reasons)))) failure = "Routes not found.\n" failure += "Looking for {prefix} on {inst} {criteria}:\n\t".format( prefix=prefix, inst=inst.name, criteria=", ".join(criteria)) failure += "\n\t".join([ err_msg.format( inst=inst.name, prefix=prefix, other_inst="{} ({})".format(other_inst.ip, other_inst.name) if other_inst else "", as_path=as_path, next_hop_ip=next_hop_ip, std_comms=std_comms, lrg_comms=lrg_comms, ext_comms=ext_comms, local_pref=local_pref, ) for err_msg in errors ]) failure += self._instance_log_contains_errors_warning(inst) self.fail(failure)