def __init__(self, **kwargs): global args default_vals = { 'host': None, 'port': None, 'entries': 32000, 'percent': 10, 'loops': 100, 'minprefix': 16, 'maxprefix': 64, 'randseed': 0, # we're going for pseudorandom, not real random } default_vals.update(kwargs) for kw, arg in default_vals.items(): setattr(self, kw, arg) self.routes = {} self.generation = 1 if (self.host is None) or (self.port is None): raise Exception("Test needs to specify 'host' and 'port' options") if (self.maxprefix > 120): raise Exception("Error: this tool is not yet smart" + " enough to do maxprefix > 120") random.seed(self.randseed) self.client = FbossAgentClient(host=self.host, port=self.port) # a list of next hops; all routes point to same one: DROP self.nexthops = [utils.ip_to_binary(nh) for nh in []] self.client_id = 31336 # ID for our routes
def convert(self, value, param, ctx): try: if self.port_info_map is None: client = FbossAgentClient(ctx.obj.hostname) self.port_info_map = client.getAllPortInfo() if value.isdigit(): port = self.port_info_map[int(value)] return port.portId for port_id, port_info in self.port_info_map.items(): if port_info.name == value: return port_id raise ValueError("No port found with that name") except (ValueError, KeyError): self.fail('%s is not a valid Port' % value, param, ctx)
class PortStatusTest(unittest.TestCase): def setUp(self): self.client = FbossAgentClient(host=args['host'], port=args['port']) def test_port_status_matchfb303(self): """ Verify that for each port, it's internal status (down, up) matches what we're reporting to fb303. See t14145409 """ for _pnum, pstate in self.client.getAllPortInfo().items(): self.assertEqual(pstate.operState, self.client.getCounter("%s.up" % pstate.name)) def tearDown(self): # TODO(rsher) figure out better cleanup self.client.__exit__(None, None, None)
def verify_switch(self): """ Verify the switch is THRIFT reachable """ try: with FbossAgentClient(self.switch, self.port) as client: client.keepalive() # will throw FbossBaseError on failure except FbossBaseError: return False return True
def __init__(self, **kwargs): for kw, arg in StressRouteInsertion.defaults.items(): setattr(self, kw, arg) for kw, arg in kwargs.items(): setattr(self, kw, arg) self.routes = {} self.generation = 1 if (self.host is None) or (self.port is None): raise Exception("Test needs to specify 'host' and 'port' options") if (self.maxprefix > 120): raise Exception("Error: this tool is not yet smart" + " enough to do maxprefix > 120") random.seed(self.randseed) self.client = FbossAgentClient(host=self.host, port=self.port) # a list of next hops; all routes point to same one: DROP self.nexthops = [utils.ip_to_binary(nh) for nh in []] self.client_id = 31336 # ID for our routes
def get_fb303_counter(switch_thrift: FbossAgentClient, counter: str): """ @param switch_thrift: FbossAgentClient object that is part of test topology @param counter: a string that identifies fb303 counter getCounter will raise an exception on failure. For now we only retry on the list of known exceptions above. If any other exception happens, we die """ return switch_thrift.getCounter(counter)
def verify_switch(self, log=None): """ Verify the switch is THRIFT reachable """ if not log: log = self.log try: with FbossAgentClient(self.switch.name, self.port) as client: client.keepalive() # will throw FbossBaseError on failure except (FbossBaseError, TTransportException): log.warning("Switch failed to thrift verify") return False return True
def _create_agent_client(self): args = [self._hostname, self._port] if self._timeout: args.append(self._timeout) if self._snapshot_file is not None: snap_client = pickle.load(open(self._snapshot_file, "rb")) try: return snap_client[self._hostname]['agent'] except KeyError: print("Please specify the host the snapshot was taken of") exit(0) return FbossAgentClient(*args)
def get_fb303_counter(switch_thrift: FbossAgentClient, counter: str, log: Logger, try_number: int): """ @param switch_thrift: FbossAgentClient object that is part of test topology @param counter: a string that identifies fb303 counter @param logger: Logger object @param try_number: int passed in by retryable to identified current try getCounter will raise an exception on failure. For now we only retry on the list of known exceptions above. If any other exception happens, we die """ log.info( f"Trying to get counter {counter}, try #{try_number}" ) return switch_thrift.getCounter(counter)
def verify_switch(self, retries=10, log=None): """ Verify the switch is THRIFT reachable """ if not log: log = self.log """ Verfiy that both userver and agent are reachable """ # check if userver is reachable, tests can bring down userver # Example: T33276019 if not is_host_ping_reachable(self.switch.name, log): return False """ Verify the switch is THRIFT reachable """ for retry in range(retries): # don't use @retry, this is OSS try: with FbossAgentClient(self.switch.name, self.port) as client: client.keepalive( ) # simple check, will pass if agent is up # this actually checks that agent is in a ready state client.getAllPortInfo() # will throw FbossBaseError return True except (FbossBaseError, TTransportException) as e: log.warning("Switch failed to thrift verify (try #%d): %s" % (retry, str(e))) time.sleep(1) return False
def setUp(self): self.client = FbossAgentClient(host=args['host'], port=args['port'])
class StressRouteInsertion(object): """ Measure latency of bulk route thrashing. Algorithm: 1) Insert $entries randomly generated v6 route entries 2) For loop = 1 to $loops: a) remove $percent percent of the routes, randomly b) generate a new set of $percent routes b) start the clock c) add new routes d) stop the clock and record the time The theory is this roughly emulates worst case of BGP sessions coming and going over time. The $percent variable should correspond to a worst case guess as to the number of routes in a peering session. """ def __init__(self, **kwargs): global args default_vals = { 'host': None, 'port': None, 'entries': 32000, 'percent': 10, 'loops': 100, 'minprefix': 16, 'maxprefix': 64, 'randseed': 0, # we're going for pseudorandom, not real random } default_vals.update(kwargs) for kw, arg in default_vals.items(): setattr(self, kw, arg) self.routes = {} self.generation = 1 if (self.host is None) or (self.port is None): raise Exception("Test needs to specify 'host' and 'port' options") if (self.maxprefix > 120): raise Exception("Error: this tool is not yet smart" + " enough to do maxprefix > 120") random.seed(self.randseed) self.client = FbossAgentClient(host=self.host, port=self.port) # a list of next hops; all routes point to same one: DROP self.nexthops = [utils.ip_to_binary(nh) for nh in []] self.client_id = 31336 # ID for our routes def generate_random_routes(self, n=None): # store routes as a dict to prevent duplications if n is None: n = self.entries routes = {} while len(routes) < n: routes[self.gen_rand_route()] = self.generation return routes def gen_rand_route(self): """ Generate a random IPv6 route as a string @NOTE: doesn't work for prefix > 120 bits""" prefix = random.randint(self.minprefix, self.maxprefix) # inclusive r = "" for i in range(0, int(prefix / 4)): r += "{0:x}".format(random.randint(0, 15)) if ((i + 1) % 4) == 0: r += ":" leftover = prefix - (i * 4) # this ensures we don't end with a ':' as well r += "{0:x}".format(random.randint(0, 15) & (pow(2, leftover) - 1)) return r + "::1/{}".format(prefix) def insert_route(self, route): ip, prefix = route.split("/") addr = utils.ip_to_binary(ip) ipRoute = IpPrefix(ip=addr, prefixLength=prefix) uniRoute = UnicastRoute(dest=ipRoute, nextHopAddrs=self.nexthops) self.client.addUnicastRoute(self.client_id, uniRoute) def delete_route(self, route): ip, prefix = route.split("/") addr = utils.ip_to_binary(ip) ipRoute = IpPrefix(ip=addr, prefixLength=prefix) self.client.deleteUnicastRoute(self.client_id, ipRoute) def clean_up(self): for route in self.routes: self.delete_route(route) def run_test(self): """ Run actual test """ print( "Generating {} random routes with prefix between {} and {}".format( self.entries, self.minprefix, self.maxprefix)) self.routes = self.generate_random_routes() print("Inserting initial routes into the switch...") start = time.clock() for route in self.routes: self.insert_route(route) stop = time.clock() print(" ... done : {} seconds - not the real test, but FYI".format( stop - start)) target = (1 - (self.percent / 100)) * self.entries for loop in range(0, self.loops): print("--- Starting loop {}...".format(loop)) print("Deleting {} routes".format(self.entries - target)) while len(self.routes) > target: route = random.choice(list(self.routes.keys())) self.delete_route(route) del self.routes[route] print("Picking {} new routes".format(self.entries - target)) new_routes = self.generate_random_routes(n=self.entries - target) print("Adding new routes") start = time.clock() for route in new_routes: self.routes[route] = loop self.insert_route(route) stop = time.clock() print("RESULT: {} seconds to add {} new routes".format( stop - start, self.entries - target))
def _create_ctrl_client(self): args = [self._hostname, self._port] if self._timeout: args.append(self._timeout) return FbossAgentClient(*args)
def switch_thrift(self, port=None): port = port or self.port return FbossAgentClient(self.switch.name, port=port)
class StressRouteInsertion(object): """ Measure latency of bulk route thrashing. Algorithm: 1) Insert $entries randomly generated v6 route entries 2) For loop = 1 to $loops: a) remove $percent percent of the routes, randomly b) generate a new set of $percent routes b) start the clock c) add new routes d) stop the clock and record the time The theory is this roughly emulates worst case of BGP sessions coming and going over time. The $percent variable should correspond to a worst case guess as to the number of routes in a peering session. """ defaults = { 'host': 'localhost', 'port': 5909, 'entries': 4000, 'percent': 10, 'loops': 5, 'minprefix': 16, 'maxprefix': 64, 'pause_on_exit': False, 'randseed': 0, # we're going for pseudorandom, not real random } def __init__(self, **kwargs): for kw, arg in StressRouteInsertion.defaults.items(): setattr(self, kw, arg) for kw, arg in kwargs.items(): setattr(self, kw, arg) self.routes = {} self.generation = 1 if (self.host is None) or (self.port is None): raise Exception("Test needs to specify 'host' and 'port' options") if (self.maxprefix > 120): raise Exception("Error: this tool is not yet smart" + " enough to do maxprefix > 120") random.seed(self.randseed) self.client = FbossAgentClient(host=self.host, port=self.port) # a list of next hops; all routes point to same one: DROP self.nexthops = [utils.ip_to_binary(nh) for nh in []] self.client_id = 31336 # ID for our routes def generate_random_routes(self, n=None): # store routes as a dict to prevent duplications if n is None: n = self.entries routes = {} while len(routes) < n: routes[self.gen_rand_route()] = self.generation return routes def gen_rand_route(self): """ Generate a random IPv6 route as a string @NOTE: doesn't work for prefix > 120 bits""" prefix = random.randint(self.minprefix, self.maxprefix) # inclusive r = "" for i in range(0, int(prefix / 4)): r += "{0:x}".format(random.randint(0, 15)) if ((i + 1) % 4) == 0: r += ":" leftover = prefix - (i * 4) # this ensures we don't end with a ':' as well r += "{0:x}".format(random.randint(0, 15) & (pow(2, leftover) - 1)) return r + "::1/{}".format(prefix) def insert_routes(self, routes): uniRoutes = [] for route in routes: ip, prefix = route.split("/") addr = utils.ip_to_binary(ip) ipRoute = IpPrefix(ip=addr, prefixLength=prefix) uniRoutes.append(UnicastRoute(dest=ipRoute, nextHopAddrs=self.nexthops)) self.client.addUnicastRoutes(self.client_id, uniRoutes) def delete_routes(self, routes): uniRoutes = [] for route in routes: ip, prefix = route.split("/") addr = utils.ip_to_binary(ip) uniRoutes.append(IpPrefix(ip=addr, prefixLength=prefix)) self.client.deleteUnicastRoutes(self.client_id, uniRoutes) def clean_up(self): print("Removing all routes") self.delete_routes(self.routes) self.client._socket.close() print("...done.") def run_test(self): """ Run actual test """ print("Generating {} random routes with prefix between {} and {}".format( self.entries, self.minprefix, self.maxprefix)) self.routes = self.generate_random_routes() print("... done.") print("Inserting initial routes into the switch...") start = time.clock() self.insert_routes(self.routes) stop = time.clock() print(" ... done : {} seconds - not the real test, but FYI".format( stop - start)) target = (1 - (self.percent / 100)) * self.entries for loop in range(0, self.loops): print("--- Starting loop {}...".format(loop)) print("Deleting {} routes".format(self.entries - target)) delete_routes = [] while len(self.routes) > target: route = random.choice(list(self.routes.keys())) delete_routes.append(route) del self.routes[route] self.delete_routes(delete_routes) print("Picking {} new routes".format(self.entries - target)) new_routes = self.generate_random_routes(n=self.entries - target) print("Adding new routes") start = time.clock() for route in new_routes: self.routes[route] = loop self.insert_routes(new_routes) stop = time.clock() print("RESULT: {} seconds to add {} new routes".format( stop - start, self.entries - target)) if self.pause_on_exit: input("\n\n\nTest Done -- press return to cleanup: ")
def switch_thrift(self): return FbossAgentClient(self.switch, self.port)
def setUp(self): self.client = FbossAgentClient(host=args.host, port=args.port)