diff options
author | ahothan <ahothan@cisco.com> | 2018-04-24 10:06:34 -0700 |
---|---|---|
committer | ahothan <ahothan@cisco.com> | 2018-04-24 10:39:33 -0700 |
commit | ca854882fe536396857ca25e84d7f18ea769c595 (patch) | |
tree | 4070e998b598f6634d74a28d82a7a729b933360f | |
parent | 3918ebaca624bf6d58559d602eed8e33881e7ce0 (diff) |
[NFVBENCH-86] In case of lossy loopback device, packet drops are reported incorrectly1.4.0
This is only for fixed rate runs, the chain analysis table at end of run may contain
incorrect drops.
Change-Id: I6656528ed695a60003c672132624a7284db60497
Signed-off-by: ahothan <ahothan@cisco.com>
-rw-r--r-- | nfvbench/chain_managers.py | 72 | ||||
-rw-r--r-- | nfvbench/network.py | 29 | ||||
-rwxr-xr-x | nfvbench/traffic_client.py | 36 |
3 files changed, 88 insertions, 49 deletions
diff --git a/nfvbench/chain_managers.py b/nfvbench/chain_managers.py index de6afca..ab340bf 100644 --- a/nfvbench/chain_managers.py +++ b/nfvbench/chain_managers.py @@ -15,7 +15,6 @@ # import time - from log import LOG from network import Network from packet_analyzer import PacketAnalyzer @@ -24,6 +23,7 @@ from stats_collector import IntervalCollector class StageManager(object): + """A class to stage resources in the systenm under test.""" def __init__(self, config, cred, factory): self.config = config @@ -67,7 +67,8 @@ class StageManager(object): self.client.dispose() -class StatsManager(object): +class PVPStatsManager(object): + """A class to generate traffic and extract results for PVP chains.""" def __init__(self, config, clients, specs, factory, vlans, notifier=None): self.config = config @@ -98,10 +99,22 @@ class StatsManager(object): def _get_data(self): return self.worker.get_data() if self.worker else {} - def _get_network(self, traffic_port, index, stats, reverse=False): + def _get_network(self, traffic_port, stats, reverse=False): + """Get the Network object corresponding to a given TG port. + + :param traffic_port: must be either 0 or 1 + :param stats: TG stats for given traffic port + :param reverse: specifies if the interface list for this network + should go from TG to loopback point (reverse=false) or + from loopback point to TG (reverse=true) + """ + # build the interface list in fwd direction (TG To loopback point) interfaces = [self.clients['traffic'].get_interface(traffic_port, stats)] if self.worker: - interfaces.extend(self.worker.get_network_interfaces(index)) + # if available, + # interfaces for workers must be aligned on the TG port number + interfaces.extend(self.worker.get_network_interfaces(traffic_port)) + # let Network reverse the interface order if needed return Network(interfaces, reverse) def _config_interfaces(self): @@ -131,9 +144,7 @@ class StatsManager(object): return self.worker.get_version() if self.worker else {} def run(self): - """ - Run analysis in both direction and return the analysis - """ + """Run analysis in both direction and return the analysis.""" if self.worker: self.worker.run() @@ -145,20 +156,17 @@ class StatsManager(object): } # fetch latest stats from traffic gen - if self.config.no_traffic: - stats = None - else: - stats = self.clients['traffic'].get_stats() + stats = self.clients['traffic'].get_stats() LOG.info('Requesting packet analysis on the forward direction...') result['packet_analysis']['direction-forward'] = \ - self.get_analysis([self._get_network(0, 0, stats), - self._get_network(0, 1, stats, reverse=True)]) + self.get_analysis([self._get_network(0, stats), + self._get_network(1, stats, reverse=True)]) LOG.info('Packet analysis on the forward direction completed') LOG.info('Requesting packet analysis on the reverse direction...') result['packet_analysis']['direction-reverse'] = \ - self.get_analysis([self._get_network(1, 1, stats), - self._get_network(1, 0, stats, reverse=True)]) + self.get_analysis([self._get_network(1, stats), + self._get_network(0, stats, reverse=True)]) LOG.info('Packet analysis on the reverse direction completed') return result @@ -187,21 +195,14 @@ class StatsManager(object): self.worker.close() -class PVPStatsManager(StatsManager): - - def __init__(self, config, clients, specs, factory, vlans, notifier=None): - StatsManager.__init__(self, config, clients, specs, factory, vlans, notifier) - - -class PVVPStatsManager(StatsManager): +class PVVPStatsManager(PVPStatsManager): + """A Class to generate traffic and extract results for PVVP chains.""" def __init__(self, config, clients, specs, factory, vlans, notifier=None): - StatsManager.__init__(self, config, clients, specs, factory, vlans, notifier) + PVPStatsManager.__init__(self, config, clients, specs, factory, vlans, notifier) def run(self): - """ - Run analysis in both direction and return the analysis - """ + """Run analysis in both direction and return the analysis.""" fwd_v2v_net, rev_v2v_net = self.worker.run() stats = self._generate_traffic() @@ -211,19 +212,16 @@ class PVVPStatsManager(StatsManager): 'stats': stats } # fetch latest stats from traffic gen - if self.config.no_traffic: - stats = None - else: - stats = self.clients['traffic'].get_stats() - fwd_nets = [self._get_network(0, 0, stats)] + stats = self.clients['traffic'].get_stats() + fwd_nets = [self._get_network(0, stats)] if fwd_v2v_net: fwd_nets.append(fwd_v2v_net) - fwd_nets.append(self._get_network(0, 1, stats, reverse=True)) + fwd_nets.append(self._get_network(1, stats, reverse=True)) - rev_nets = [self._get_network(1, 1, stats)] + rev_nets = [self._get_network(1, stats)] if rev_v2v_net: rev_nets.append(rev_v2v_net) - rev_nets.append(self._get_network(1, 0, stats, reverse=True)) + rev_nets.append(self._get_network(0, stats, reverse=True)) LOG.info('Requesting packet analysis on the forward direction...') result['packet_analysis']['direction-forward'] = self.get_analysis(fwd_nets) @@ -236,9 +234,11 @@ class PVVPStatsManager(StatsManager): return result -class EXTStatsManager(StatsManager): +class EXTStatsManager(PVPStatsManager): + """A Class to generate traffic and extract results for EXT chains.""" + def __init__(self, config, clients, specs, factory, vlans, notifier=None): - StatsManager.__init__(self, config, clients, specs, factory, vlans, notifier) + PVPStatsManager.__init__(self, config, clients, specs, factory, vlans, notifier) def _setup(self): if self.specs.openstack: diff --git a/nfvbench/network.py b/nfvbench/network.py index e097c2b..6c02f04 100644 --- a/nfvbench/network.py +++ b/nfvbench/network.py @@ -15,8 +15,10 @@ class Interface(object): + """A class to hold the RX and TX counters for a virtual or physical interface.""" def __init__(self, name, device, tx_packets, rx_packets): + """Create a new interface instance.""" self.name = name self.device = device self.packets = { @@ -25,38 +27,65 @@ class Interface(object): } def set_packets(self, tx, rx): + """Set tx and rx counters for this interface.""" self.packets = { 'tx': tx, 'rx': rx } def set_packets_diff(self, tx, rx): + """Subtract current counters from new set of counters and update with results.""" self.packets = { 'tx': tx - self.packets['tx'], 'rx': rx - self.packets['rx'], } def is_no_op(self): + """Check if this interface is a no-opn interface.""" return self.name is None def get_packet_count(self, traffic_type): + """Get packet count for given direction.""" return self.packets.get(traffic_type, 0) @staticmethod def no_op(): + """Return an interface that doe snot pass any traffic.""" return Interface(None, None, 0, 0) class Network(object): + """This class holds all interfaces that make up a logical neutron network. + + A loopback packet path has exactly 2 networks. + The first interface is always one of the 2 traffic gen interface. + Subsequent interfaces are sorted along the path from the TG to the loopback point + which could be interfaces in a switch, a vswitch or a VM. + """ def __init__(self, interfaces=None, reverse=False): + """Create a network with initial interface list and direction. + + :param interfaces: initial interface list + :param reverse: specifies the order of interfaces returned by get_interfaces + """ if interfaces is None: interfaces = [] self.interfaces = interfaces self.reverse = reverse def add_interface(self, interface): + """Add one more interface to this network. + + Order if important as interfaces must be added from traffic generator ports towards then + looping back device. + """ self.interfaces.append(interface) def get_interfaces(self): + """Get interfaces associated to this network. + + Returned interface list is ordered from traffic generator port towards looping device if + reverse is false. Else returms the list in the reverse order. + """ return self.interfaces[::-1] if self.reverse else self.interfaces diff --git a/nfvbench/traffic_client.py b/nfvbench/traffic_client.py index 2ce118c..25fb871 100755 --- a/nfvbench/traffic_client.py +++ b/nfvbench/traffic_client.py @@ -35,10 +35,14 @@ from utils import cast_integer class TrafficClientException(Exception): + """Generic traffic client exception.""" + pass class TrafficRunner(object): + """Serialize various steps required to run traffic.""" + def __init__(self, client, duration_sec, interval_sec=0): self.client = client self.start_time = None @@ -89,6 +93,8 @@ class TrafficRunner(object): class IpBlock(object): + """Manage a block of IP addresses.""" + def __init__(self, base_ip, step_ip, count_ip): self.base_ip_int = Device.ip_to_int(base_ip) self.step = Device.ip_to_int(step_ip) @@ -96,15 +102,13 @@ class IpBlock(object): self.next_free = 0 def get_ip(self, index=0): - '''Return the IP address at given index - ''' + """Return the IP address at given index.""" if index < 0 or index >= self.max_available: raise IndexError('Index out of bounds') return Device.int_to_ip(self.base_ip_int + index * self.step) def reserve_ip_range(self, count): - '''Reserve a range of count consecutive IP addresses spaced by step - ''' + """Reserve a range of count consecutive IP addresses spaced by step.""" if self.next_free + count > self.max_available: raise IndexError('No more IP addresses next free=%d max_available=%d requested=%d' % (self.next_free, @@ -120,6 +124,8 @@ class IpBlock(object): class Device(object): + """Represent a port device and all information associated to it.""" + def __init__(self, port, pci, switch_port=None, vtep_vlan=None, ip=None, tg_gateway_ip=None, gateway_ip=None, ip_addrs_step=None, tg_gateway_ip_addrs_step=None, gateway_ip_addrs_step=None, udp_src_port=None, udp_dst_port=None, @@ -158,6 +164,7 @@ class Device(object): if mac is None: raise TrafficClientException('Trying to set traffic generator MAC address as None') self.mac = mac + LOG.info("Port %d: src MAC %s", self.port, self.mac) def set_destination(self, dst): self.dst = dst @@ -169,10 +176,10 @@ class Device(object): if self.vlan_tagging and vlan_tag is None: raise TrafficClientException('Trying to set VLAN tag as None') self.vlan_tag = vlan_tag + LOG.info("Port %d: VLAN %d", self.port, self.vlan_tag) def get_gw_ip(self, chain_index): - '''Retrieve the IP address assigned for the gateway of a given chain - ''' + """Retrieve the IP address assigned for the gateway of a given chain.""" return self.gw_ip_block.get_ip(chain_index) def get_stream_configs(self, service_chain): @@ -222,8 +229,7 @@ class Device(object): return configs def ip_range_overlaps(self): - '''Check if this device ip range is overlapping with the dst device ip range - ''' + """Check if this device ip range is overlapping with the dst device ip range.""" src_base_ip = Device.ip_to_int(self.ip) dst_base_ip = Device.ip_to_int(self.dst.ip) src_last_ip = src_base_ip + self.flow_count - 1 @@ -367,6 +373,8 @@ class RunningTrafficProfile(object): class TrafficGeneratorFactory(object): + """Factory class to generate a traffic generator.""" + def __init__(self, config): self.config = config @@ -406,6 +414,8 @@ class TrafficGeneratorFactory(object): class TrafficClient(object): + """Traffic generator client.""" + PORTS = [0, 1] def __init__(self, config, notifier=None, skip_sleep=False): @@ -449,8 +459,8 @@ class TrafficClient(object): return self.gen.get_version() def ensure_end_to_end(self): - """ - Ensure traffic generator receives packets it has transmitted. + """Ensure traffic generator receives packets it has transmitted. + This ensures end to end connectivity and also waits until VMs are ready to forward packets. VMs that are started and in active state may not pass traffic yet. It is imperative to make @@ -662,7 +672,7 @@ class TrafficClient(object): results[tag]['timestamp_sec'] = time.time() def __range_search(self, left, right, targets, results): - '''Perform a binary search for a list of targets inside a [left..right] range or rate + """Perform a binary search for a list of targets inside a [left..right] range or rate. left the left side of the range to search as a % the line rate (100 = 100% line rate) indicating the rate to send on each interface @@ -671,7 +681,7 @@ class TrafficClient(object): targets a dict of drop rates to search (0.1 = 0.1%), indexed by the DR name or "tag" ('ndr', 'pdr') results a dict to store results - ''' + """ if not targets: return LOG.info('Range search [%s .. %s] targets: %s', left, right, targets) @@ -820,7 +830,7 @@ class TrafficClient(object): return config def get_run_config(self, results): - """Returns configuration which was used for the last run.""" + """Return configuration which was used for the last run.""" r = {} for idx, key in enumerate(["direction-forward", "direction-reverse"]): tx_rate = results["stats"][idx]["tx"]["total_pkts"] / self.config.duration_sec |