This is required when shared net is used and there are more VMs running than requested in the -scc
Change-Id: I7599169739e6bb9b3e2377473377d5332ef2b68a
Signed-off-by: ahothan <ahothan@cisco.com>
return self.generator_config.devices[1 - self.port]
def set_dest_macs(self, dest_macs):
- """Set the list of dest MACs indexed by the chain id."""
+ """Set the list of dest MACs indexed by the chain id.
+
+ This is only called in 2 cases:
+ - VM macs discovered using openstack API
+ - dest MACs provisioned in config file
+ """
self.dest_macs = map(str, dest_macs)
+ def get_dest_macs(self):
+ """Get the list of dest macs for this device.
+
+ If set_dest_macs was never called, assumes l2-loopback and return
+ a list of peer mac (as many as chains but normally only 1 chain)
+ """
+ if self.dest_macs:
+ return self.dest_macs
+ # assume this is l2-loopback
+ return [self.get_peer_device().mac] * self.chain_count
+
def set_vlans(self, vlans):
"""Set the list of vlans to use indexed by the chain id."""
self.vlans = vlans
peer = self.get_peer_device()
self.ip_block.reset_reservation()
peer.ip_block.reset_reservation()
+ dest_macs = self.get_dest_macs()
for chain_idx in xrange(self.chain_count):
src_ip_first, src_ip_last = self.ip_block.reserve_ip_range(cur_chain_flow_count)
dst_ip_first, dst_ip_last = peer.ip_block.reserve_ip_range(cur_chain_flow_count)
- dest_mac = self.dest_macs[chain_idx] if self.dest_macs else peer.mac
configs.append({
'count': cur_chain_flow_count,
'mac_src': self.mac,
- 'mac_dst': dest_mac,
+ 'mac_dst': dest_macs[chain_idx],
'ip_src_addr': src_ip_first,
'ip_src_addr_max': src_ip_last,
'ip_src_count': cur_chain_flow_count,
self.devices[port_index].set_dest_macs(dest_macs)
LOG.info('Port %d: dst MAC %s', port_index, [str(mac) for mac in dest_macs])
+ def get_dest_macs(self):
+ """Return the list of dest macs indexed by port."""
+ return [dev.get_dest_macs() for dev in self.devices]
+
def set_vlans(self, port_index, vlans):
"""Set the list of vlans to use indexed by the chain id on given port.
# ensures enough traffic is coming back
retry_count = (self.config.check_traffic_time_sec +
self.config.generic_poll_sec - 1) / self.config.generic_poll_sec
- mac_addresses = set()
# we expect to see packets coming from 2 unique MAC per chain
- unique_src_mac_count = self.config.service_chain_count * 2
+ # because there can be flooding in the case of shared net
+ # we must verify that packets from the right VMs are received
+ # and not just count unique src MAC
+ # create a dict of (port, chain) tuples indexed by dest mac
+ mac_map = {}
+ for port, dest_macs in enumerate(self.generator_config.get_dest_macs()):
+ for chain, mac in enumerate(dest_macs):
+ mac_map[mac] = (port, chain)
+ unique_src_mac_count = len(mac_map)
for it in xrange(retry_count):
self.gen.clear_stats()
self.gen.start_traffic()
self.gen.start_capture()
LOG.info('Captured unique src mac %d/%d, capturing return packets (retry %d/%d)...',
- len(mac_addresses), unique_src_mac_count,
+ unique_src_mac_count - len(mac_map), unique_src_mac_count,
it + 1, retry_count)
if not self.skip_sleep():
time.sleep(self.config.generic_poll_sec)
for packet in self.gen.packet_list:
src_mac = packet['binary'][6:12]
- if src_mac not in mac_addresses:
- LOG.info('Received packet from mac: %s',
- ':'.join(["%02x" % ord(x) for x in src_mac]))
- mac_addresses.add(src_mac)
-
- if len(mac_addresses) == unique_src_mac_count:
+ src_mac = ':'.join(["%02x" % ord(x) for x in src_mac])
+ if src_mac in mac_map:
+ port, chain = mac_map[src_mac]
+ LOG.info('Received packet from mac: %s (chain=%d, port=%d)',
+ src_mac, chain, port)
+ mac_map.pop(src_mac, None)
+
+ if not mac_map:
LOG.info('End-to-end connectivity established')
return
def ensure_arp_successful(self):
"""Resolve all IP using ARP and throw an exception in case of failure."""
- if not self.gen.resolve_arp():
+ dest_macs = self.gen.resolve_arp()
+ if dest_macs:
+ # all dest macs are discovered, saved them into the generator config
+ self.generator_config.set_dest_macs(0, dest_macs[0])
+ self.generator_config.set_dest_macs(1, dest_macs[1])
+ else:
raise TrafficClientException('ARP cannot be resolved')
def set_traffic(self, frame_size, bidirectional):
self.duration_sec = traffic_client.config.duration_sec
self.intf_speed = traffic_client.generator_config.intf_speed
self.set_response_curve()
- # for packet capture, generate 2*scc random packets
- # normally we should generate packets coming from the right dest macs
- scc = traffic_client.config.service_chain_count
- self.packet_list = [self._get_packet_capture(mac_id) for mac_id in range(scc * 2)]
-
- def _get_packet_capture(self, mac_id):
- return {'binary': 'SSSSSS01234' + str(mac_id)}
+ self.packet_list = None
def get_version(self):
return "0.1"
latencies[port].avg_usec = 50
def get_macs(self):
- return ['00.00.00.00.00.01', '00.00.00.00.00.02']
+ return ['00:00:00:00:00:01', '00:00:00:00:00:02']
def get_port_speed_gbps(self):
"""Return the local port speeds.
pass
def fetch_capture_packets(self):
- pass
+ def _get_packet_capture(mac):
+ # convert text to binary
+ src_mac = mac.replace(':', '').decode('hex')
+ return {'binary': 'SSSSSS' + src_mac}
+
+ # for packet capture, generate 2*scc random packets
+ # normally we should generate packets coming from the right dest macs
+ self.packet_list = []
+ for dest_macs in self.traffic_client.generator_config.get_dest_macs():
+ for mac in dest_macs:
+ self.packet_list.append(_get_packet_capture(mac))
def stop_traffic(self):
pass
def resolve_arp(self):
"""Resolve ARP sucessfully."""
- LOG.info('Dummy TG ARP OK')
- return True
+ def get_macs(port, scc):
+ return ['00:00:00:00:%02x:%02x' % (port, chain) for chain in range(scc)]
+ scc = self.traffic_client.generator_config.service_chain_count
+ res = [get_macs(port, scc) for port in range(2)]
+ LOG.info('Dummy TG ARP: %s', str(res))
+ return res
def resolve_arp(self):
"""Resolve all configured remote IP addresses.
- return: True if ARP resolved successfully
+ return: None if ARP failed to resolve for all IP addresses
+ else a dict of list of dest macs indexed by port#
+ the dest macs in the list are indexed by the chain id
"""
pass
self.port_handle = []
self.chain_count = self.generator_config.service_chain_count
self.rates = []
- # A dict of list of dest macs indexed by port#
- # the dest macs in the list are indexed by the chain id
- self.arps = {}
self.capture_id = None
self.packet_list = []
def resolve_arp(self):
"""Resolve all configured remote IP addresses.
- return: True if ARP resolved successfully
+ return: None if ARP failed to resolve for all IP addresses
+ else a dict of list of dest macs indexed by port#
+ the dest macs in the list are indexed by the chain id
"""
self.client.set_service_mode(ports=self.port_handle)
LOG.info('Polling ARP until successful...')
self.client.set_service_mode(ports=self.port_handle, enabled=False)
if len(arps) == len(self.port_handle):
- self.arps = arps
- return True
- return False
+ return arps
+ return None
def __is_rate_enough(self, l2frame_size, rates, bidirectional, latency):
"""Check if rate provided by user is above requirements. Applies only if latency is True."""
stream_cfgs = [d.get_stream_configs() for d in self.generator_config.devices]
self.rates = [utils.to_rate_str(rate) for rate in rates]
for chain_id, (fwd_stream_cfg, rev_stream_cfg) in enumerate(zip(*stream_cfgs)):
- if self.arps:
- # in case of external chain with ARP, fill in the proper dest MAC
- # based on the 2 ARP replies for each chain
- fwd_stream_cfg['mac_dst'] = self.arps[self.port_handle[0]][chain_id]
- rev_stream_cfg['mac_dst'] = self.arps[self.port_handle[1]][chain_id]
-
streamblock[0].extend(self.generate_streams(self.port_handle[0],
chain_id,
fwd_stream_cfg,
"""
return [port['speed'] for port in self.port_info]
- def get_dest_macs(self):
- """Return the dest MAC for all chains for both ports for the current traffic setup.
-
- return: a list of MAC addresses indexed by the port# [[m00, m01...], [m10, m11...]]
-
- If ARP are used, resolve_arp() must be called prior to calling this method.
- """
- # if ARP was used, return the dest MACs resolved by ARP
- if self.arps:
- return [self.arps[port] for port in self.port_handle]
- # no ARP, use the dest MACs as configured in the devices
- return [d.dest_macs for d in self.generator_config.devices]
-
def clear_stats(self):
"""Clear all stats in the traffic gneerator."""
if self.port_handle:
from mock import patch
from nfvbench.chain_runner import ChainRunner
+from nfvbench.chaining import ChainVnfPort
from nfvbench.compute import Compute
import nfvbench.credentials
from nfvbench.factory import BasicFactory
print res
assert res['status'] == 'OK'
+
+mac_seq = 0
+
+def _mock_get_mac(dummy):
+ global mac_seq
+ mac_seq += 1
+ return '01:00:00:00:00:%02x' % mac_seq
+
@patch.object(Compute, 'get_enabled_az_host_list', _mock_get_enabled_az_host_list)
@patch.object(Compute, 'find_image', _mock_find_image)
@patch.object(TrafficClient, 'skip_sleep', lambda x: True)
+@patch.object(ChainVnfPort, 'get_mac', _mock_get_mac)
@patch('nfvbench.chaining.Client')
@patch('nfvbench.chaining.neutronclient')
@patch('nfvbench.chaining.glanceclient')
expected = fail_pair[1]
if expected is None:
expected = fail_pair[0]
- assert expected in e_info.value.message
+ assert expected in str(e_info)
# whitelist keys
flavor = {'flavor': {'vcpus': 2, 'ram': 8192, 'disk': 0,