aboutsummaryrefslogtreecommitdiffstats
path: root/yardstick/network_services
diff options
context:
space:
mode:
Diffstat (limited to 'yardstick/network_services')
-rw-r--r--yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py54
-rw-r--r--yardstick/network_services/traffic_profile/http_ixload.py8
-rw-r--r--yardstick/network_services/traffic_profile/prox_binsearch.py4
-rw-r--r--yardstick/network_services/vnf_generic/vnf/prox_helpers.py124
-rw-r--r--yardstick/network_services/vnf_generic/vnf/sample_vnf.py2
-rw-r--r--yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py212
6 files changed, 379 insertions, 25 deletions
diff --git a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py
index 1f465bde5..b5e4172a9 100644
--- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py
+++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py
@@ -49,6 +49,19 @@ PROTOCOL_STATUS_DOWN = ['down', 'notStarted']
SUPPORTED_PROTO = [PROTO_UDP]
+SUPPORTED_DSCP_CLASSES = [
+ 'defaultPHB',
+ 'classSelectorPHB',
+ 'assuredForwardingPHB',
+ 'expeditedForwardingPHB']
+
+SUPPORTED_TOS_FIELDS = [
+ 'precedence',
+ 'delay',
+ 'throughput',
+ 'reliability'
+]
+
class Vlan(object):
def __init__(self,
@@ -198,9 +211,10 @@ class IxNextgen(object): # pragma: no cover
:param proto: IxNet protocol str representation, e.g.:
'::ixNet::OBJ-/topology:2/deviceGroup:1/ethernet:1/ipv4:L14'
- :return: (str) protocol status: 'up', 'down' or 'notStarted'
+ :return: (list) protocol status: list of sessions protocol
+ statuses which include states 'up', 'down' and 'notStarted'
"""
- return self.ixnet.getAttribute(proto, '-sessionStatus')[0]
+ return self.ixnet.getAttribute(proto, '-sessionStatus')
def is_traffic_running(self):
"""Returns true if traffic state == TRAFFIC_STATUS_STARTED"""
@@ -218,8 +232,8 @@ class IxNextgen(object): # pragma: no cover
:return: (bool) True if all protocols status is 'up', False if any
protocol status is 'down' or 'notStarted'
"""
- return all(self._get_protocol_status(proto) == PROTOCOL_STATUS_UP
- for proto in protocols)
+ return all(session_status is PROTOCOL_STATUS_UP for proto in protocols
+ for session_status in self._get_protocol_status(proto))
def is_protocols_stopped(self, protocols):
"""Returns true if all protocols statuses are in PROTOCOL_STATUS_DOWN
@@ -229,8 +243,8 @@ class IxNextgen(object): # pragma: no cover
:return: (bool) True if all protocols status is 'down' or 'notStarted',
False if any protocol status is 'up'
"""
- return all(self._get_protocol_status(proto) in PROTOCOL_STATUS_DOWN
- for proto in protocols)
+ return all(session_status in PROTOCOL_STATUS_DOWN for proto in protocols
+ for session_status in self._get_protocol_status(proto))
@staticmethod
def _parse_framesize(framesize):
@@ -597,23 +611,25 @@ class IxNextgen(object): # pragma: no cover
'precedence': [1, 4, 7]
}
"""
- if 'raw' in priority:
+ if priority.get('raw'):
priority_field = self._get_field_in_stack_item(ip_descriptor,
'priority.raw')
self._set_priority_field(priority_field, priority['raw'])
- elif 'dscp' in priority:
+ elif priority.get('dscp'):
for field, value in priority['dscp'].items():
- priority_field = self._get_field_in_stack_item(
- ip_descriptor,
- 'priority.ds.phb.{field}.{field}'.format(field=field))
- self._set_priority_field(priority_field, value)
+ if field in SUPPORTED_DSCP_CLASSES:
+ priority_field = self._get_field_in_stack_item(
+ ip_descriptor,
+ 'priority.ds.phb.{field}.{field}'.format(field=field))
+ self._set_priority_field(priority_field, value)
- elif 'tos' in priority:
+ elif priority.get('tos'):
for field, value in priority['tos'].items():
- priority_field = self._get_field_in_stack_item(
- ip_descriptor, 'priority.tos.' + field)
- self._set_priority_field(priority_field, value)
+ if field in SUPPORTED_TOS_FIELDS:
+ priority_field = self._get_field_in_stack_item(
+ ip_descriptor, 'priority.tos.' + field)
+ self._set_priority_field(priority_field, value)
def _set_priority_field(self, field_descriptor, value):
"""Set the priority field described by field_descriptor
@@ -938,7 +954,7 @@ class IxNextgen(object): # pragma: no cover
self.ixnet.commit()
return obj
- def add_pppox_client(self, xproto, auth, user, pwd):
+ def add_pppox_client(self, xproto, auth, user, pwd, enable_redial=True):
log.debug(
"add_pppox_client: xproto='%s', auth='%s', user='%s', pwd='%s'",
xproto, auth, user, pwd)
@@ -958,6 +974,10 @@ class IxNextgen(object): # pragma: no cover
else:
raise NotImplementedError()
+ if enable_redial:
+ redial = self.ixnet.getAttribute(obj, '-enableRedial')
+ self.ixnet.setAttribute(redial + '/singleValue', '-value', 'true')
+
self.ixnet.commit()
return obj
diff --git a/yardstick/network_services/traffic_profile/http_ixload.py b/yardstick/network_services/traffic_profile/http_ixload.py
index c64e7511f..b88aadff7 100644
--- a/yardstick/network_services/traffic_profile/http_ixload.py
+++ b/yardstick/network_services/traffic_profile/http_ixload.py
@@ -278,8 +278,12 @@ class IXLOADHttpTest(object):
:param param: (dict) http_client section from traffic profile
:return:
"""
- self.update_page_size(net_traffic, param["page_object"])
- self.update_user_count(net_traffic, param["simulated_users"])
+ page = param.get("page_object")
+ if page:
+ self.update_page_size(net_traffic, page)
+ users = param.get("simulated_users")
+ if users:
+ self.update_user_count(net_traffic, users)
def update_page_size(self, net_traffic, page_object):
"""Update page_object field in http client object in net_traffic
diff --git a/yardstick/network_services/traffic_profile/prox_binsearch.py b/yardstick/network_services/traffic_profile/prox_binsearch.py
index f924cf419..402bf741c 100644
--- a/yardstick/network_services/traffic_profile/prox_binsearch.py
+++ b/yardstick/network_services/traffic_profile/prox_binsearch.py
@@ -168,8 +168,8 @@ class ProxBinSearchProfile(ProxProfile):
samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples)
- if theor_max_thruput < samples["TxThroughput"]:
- theor_max_thruput = samples['TxThroughput']
+ if theor_max_thruput < samples["RequestedTxThroughput"]:
+ theor_max_thruput = samples['RequestedTxThroughput']
samples['theor_max_throughput'] = theor_max_thruput
samples["rx_total"] = int(result.rx_total)
diff --git a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py
index 8d721c045..e9d83623b 100644
--- a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py
+++ b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py
@@ -601,6 +601,99 @@ class ProxSocketHelper(object):
LOG.debug("Multi port packet ..OK.. %s", tot_result)
return True, tot_result
+ @staticmethod
+ def multi_port_stats_tuple(stats, ports):
+ """
+ Create a statistics tuple from port stats.
+
+ Returns a dict with contains the port stats indexed by port name
+
+ :param stats: (List) - List of List of port stats in pps
+ :param ports (Iterator) - to List of Ports
+
+ :return: (Dict) of port stats indexed by port_name
+ """
+
+ samples = {}
+ port_names = {}
+ try:
+ port_names = {port_num: port_name for port_name, port_num in ports}
+ except (TypeError, IndexError, KeyError):
+ LOG.critical("Ports are not initialized or number of port is ZERO ... CRITICAL ERROR")
+ return {}
+
+ try:
+ for stat in stats:
+ port_num = stat[0]
+ samples[port_names[port_num]] = {
+ "in_packets": stat[1],
+ "out_packets": stat[2]}
+ except (TypeError, IndexError, KeyError):
+ LOG.error("Ports data and samples data is incompatable ....")
+ return {}
+
+ return samples
+
+ @staticmethod
+ def multi_port_stats_diff(prev_stats, new_stats, hz):
+ """
+ Create a statistics tuple from difference between prev port stats
+ and current port stats. And store results in pps.
+
+ :param prev_stats: (List) - Previous List of port statistics
+ :param new_stats: (List) - Current List of port statistics
+ :param hz (float) - speed of system in Hz
+
+ :return: sample (List) - Difference of prev_port_stats and
+ new_port_stats in pps
+ """
+
+ RX_TOTAL_INDEX = 1
+ TX_TOTAL_INDEX = 2
+ TSC_INDEX = 5
+
+ stats = []
+
+ if len(prev_stats) is not len(new_stats):
+ for port_index, stat in enumerate(new_stats):
+ stats.append([port_index, float(0), float(0), 0, 0, 0])
+ return stats
+
+ try:
+ for port_index, stat in enumerate(new_stats):
+ if stat[RX_TOTAL_INDEX] > prev_stats[port_index][RX_TOTAL_INDEX]:
+ rx_total = stat[RX_TOTAL_INDEX] - \
+ prev_stats[port_index][RX_TOTAL_INDEX]
+ else:
+ rx_total = stat[RX_TOTAL_INDEX]
+
+ if stat[TX_TOTAL_INDEX] > prev_stats[port_index][TX_TOTAL_INDEX]:
+ tx_total = stat[TX_TOTAL_INDEX] - prev_stats[port_index][TX_TOTAL_INDEX]
+ else:
+ tx_total = stat[TX_TOTAL_INDEX]
+
+ if stat[TSC_INDEX] > prev_stats[port_index][TSC_INDEX]:
+ tsc = stat[TSC_INDEX] - prev_stats[port_index][TSC_INDEX]
+ else:
+ tsc = stat[TSC_INDEX]
+
+ if tsc is 0:
+ rx_total = tx_total = float(0)
+ else:
+ if hz is 0:
+ LOG.error("HZ is ZERO ..")
+ rx_total = tx_total = float(0)
+ else:
+ rx_total = float(rx_total * hz / tsc)
+ tx_total = float(tx_total * hz / tsc)
+
+ stats.append([port_index, rx_total, tx_total, 0, 0, tsc])
+ except (TypeError, IndexError, KeyError):
+ stats = []
+ LOG.info("Current Port Stats incompatable to previous Port stats .. Discarded")
+
+ return stats
+
def port_stats(self, ports):
"""get counter values from a specific port"""
tot_result = [0] * 12
@@ -968,6 +1061,8 @@ class ProxResourceHelper(ClientResourceHelper):
self.step_delta = 1
self.step_time = 0.5
self._test_type = None
+ self.prev_multi_port = []
+ self.prev_hz = 0
@property
def sut(self):
@@ -1006,11 +1101,40 @@ class ProxResourceHelper(ClientResourceHelper):
def collect_collectd_kpi(self):
return self._collect_resource_kpi()
+ def collect_live_stats(self):
+ ports = []
+ for _, port_num in self.vnfd_helper.ports_iter():
+ ports.append(port_num)
+
+ ok, curr_port_stats = self.sut.multi_port_stats(ports)
+ if not ok:
+ return False, {}
+
+ hz = self.sut.hz()
+ if hz is 0:
+ hz = self.prev_hz
+ else:
+ self.prev_hz = hz
+
+ new_all_port_stats = \
+ self.sut.multi_port_stats_diff(self.prev_multi_port, curr_port_stats, hz)
+
+ self.prev_multi_port = curr_port_stats
+
+ live_stats = self.sut.multi_port_stats_tuple(new_all_port_stats,
+ self.vnfd_helper.ports_iter())
+ return True, live_stats
+
def collect_kpi(self):
result = super(ProxResourceHelper, self).collect_kpi()
# add in collectd kpis manually
if result:
result['collect_stats'] = self._collect_resource_kpi()
+
+ ok, live_stats = self.collect_live_stats()
+ if ok:
+ result.update({'live_stats': live_stats})
+
return result
def terminate(self):
diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py
index 1cb5cdd8f..8833b88f2 100644
--- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py
+++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py
@@ -898,6 +898,8 @@ class SampleVNFTrafficGen(GenericTrafficGen):
self.scenario_helper.nodes[self.name]
)
+ self.resource_helper.context_cfg = context_cfg
+
self.resource_helper.setup()
# must generate_cfg after DPDK bind because we need port number
self.resource_helper.generate_cfg()
diff --git a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py
index 89f8194c0..4c13112be 100644
--- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py
+++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py
@@ -12,7 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ipaddress
import logging
+import six
from yardstick.common import utils
from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api
@@ -25,6 +27,183 @@ LOG = logging.getLogger(__name__)
WAIT_AFTER_CFG_LOAD = 10
WAIT_FOR_TRAFFIC = 30
+WAIT_PROTOCOLS_STARTED = 360
+
+
+class IxiaBasicScenario(object):
+ def __init__(self, client, context_cfg, ixia_cfg):
+
+ self.client = client
+ self.context_cfg = context_cfg
+ self.ixia_cfg = ixia_cfg
+
+ self._uplink_vports = None
+ self._downlink_vports = None
+
+ def apply_config(self):
+ pass
+
+ def create_traffic_model(self):
+ vports = self.client.get_vports()
+ self._uplink_vports = vports[::2]
+ self._downlink_vports = vports[1::2]
+ self.client.create_traffic_model(self._uplink_vports,
+ self._downlink_vports)
+
+ def run_protocols(self):
+ pass
+
+ def stop_protocols(self):
+ pass
+
+
+class IxiaPppoeClientScenario(object):
+ def __init__(self, client, context_cfg, ixia_cfg):
+
+ self.client = client
+
+ self._uplink_vports = None
+ self._downlink_vports = None
+
+ self._access_topologies = []
+ self._core_topologies = []
+
+ self._context_cfg = context_cfg
+ self._ixia_cfg = ixia_cfg
+ self.protocols = []
+
+ def apply_config(self):
+ vports = self.client.get_vports()
+ self._uplink_vports = vports[::2]
+ self._downlink_vports = vports[1::2]
+ self._fill_ixia_config()
+ self._apply_access_network_config()
+ self._apply_core_network_config()
+
+ def create_traffic_model(self):
+ self.client.create_ipv4_traffic_model(self._access_topologies,
+ self._core_topologies)
+
+ def run_protocols(self):
+ LOG.info('PPPoE Scenario - Start Protocols')
+ self.client.start_protocols()
+ utils.wait_until_true(
+ lambda: self.client.is_protocols_running(self.protocols),
+ timeout=WAIT_PROTOCOLS_STARTED, sleep=2)
+
+ def stop_protocols(self):
+ LOG.info('PPPoE Scenario - Stop Protocols')
+ self.client.stop_protocols()
+
+ def _get_intf_addr(self, intf):
+ """Retrieve interface IP address and mask
+
+ :param intf: could be the string which represents IP address
+ with mask (e.g 192.168.10.2/24) or a dictionary with the host
+ name and the port (e.g. {'tg__0': 'xe1'})
+ :return: (tuple) pair of ip address and mask
+ """
+ if isinstance(intf, six.string_types):
+ ip, mask = tuple(intf.split('/'))
+ return ip, int(mask)
+
+ node_name, intf_name = next(iter(intf.items()))
+ node = self._context_cfg["nodes"].get(node_name, {})
+ interface = node.get("interfaces", {})[intf_name]
+ ip = interface["local_ip"]
+ mask = interface["netmask"]
+ ipaddr = ipaddress.ip_network(six.text_type('{}/{}'.format(ip, mask)),
+ strict=False)
+ return ip, ipaddr.prefixlen
+
+ def _fill_ixia_config(self):
+ pppoe = self._ixia_cfg["pppoe_client"]
+ ipv4 = self._ixia_cfg["ipv4_client"]
+
+ _ip = [self._get_intf_addr(intf)[0] for intf in pppoe["ip"]]
+ self._ixia_cfg["pppoe_client"]["ip"] = _ip
+
+ _ip = [self._get_intf_addr(intf)[0] for intf in ipv4["gateway_ip"]]
+ self._ixia_cfg["ipv4_client"]["gateway_ip"] = _ip
+
+ addrs = [self._get_intf_addr(intf) for intf in ipv4["ip"]]
+ _ip = [addr[0] for addr in addrs]
+ _prefix = [addr[1] for addr in addrs]
+
+ self._ixia_cfg["ipv4_client"]["ip"] = _ip
+ self._ixia_cfg["ipv4_client"]["prefix"] = _prefix
+
+ def _apply_access_network_config(self):
+ pppoe = self._ixia_cfg["pppoe_client"]
+ sessions_per_port = pppoe['sessions_per_port']
+ sessions_per_svlan = pppoe['sessions_per_svlan']
+ svlan_count = int(sessions_per_port / sessions_per_svlan)
+
+ # add topology per uplink port (access network)
+ for access_tp_id, vport in enumerate(self._uplink_vports):
+ name = 'Topology access {}'.format(access_tp_id)
+ tp = self.client.add_topology(name, vport)
+ self._access_topologies.append(tp)
+ # add device group per svlan
+ for dg_id in range(svlan_count):
+ s_vlan_id = int(pppoe['s_vlan']) + dg_id + access_tp_id * svlan_count
+ s_vlan = ixnet_api.Vlan(vlan_id=s_vlan_id)
+ c_vlan = ixnet_api.Vlan(vlan_id=pppoe['c_vlan'], vlan_id_step=1)
+ name = 'SVLAN {}'.format(s_vlan_id)
+ dg = self.client.add_device_group(tp, name, sessions_per_svlan)
+ # add ethernet layer to device group
+ ethernet = self.client.add_ethernet(dg, 'Ethernet')
+ self.protocols.append(ethernet)
+ self.client.add_vlans(ethernet, [s_vlan, c_vlan])
+ # add ppp over ethernet
+ if 'pap_user' in pppoe:
+ ppp = self.client.add_pppox_client(ethernet, 'pap',
+ pppoe['pap_user'],
+ pppoe['pap_password'])
+ else:
+ ppp = self.client.add_pppox_client(ethernet, 'chap',
+ pppoe['chap_user'],
+ pppoe['chap_password'])
+ self.protocols.append(ppp)
+
+ def _apply_core_network_config(self):
+ ipv4 = self._ixia_cfg["ipv4_client"]
+ sessions_per_port = ipv4['sessions_per_port']
+ sessions_per_vlan = ipv4['sessions_per_vlan']
+ vlan_count = int(sessions_per_port / sessions_per_vlan)
+
+ # add topology per downlink port (core network)
+ for core_tp_id, vport in enumerate(self._downlink_vports):
+ name = 'Topology core {}'.format(core_tp_id)
+ tp = self.client.add_topology(name, vport)
+ self._core_topologies.append(tp)
+ # add device group per vlan
+ for dg_id in range(vlan_count):
+ name = 'Core port {}'.format(core_tp_id)
+ dg = self.client.add_device_group(tp, name, sessions_per_vlan)
+ # add ethernet layer to device group
+ ethernet = self.client.add_ethernet(dg, 'Ethernet')
+ self.protocols.append(ethernet)
+ if 'vlan' in ipv4:
+ vlan_id = int(ipv4['vlan']) + dg_id + core_tp_id * vlan_count
+ vlan = ixnet_api.Vlan(vlan_id=vlan_id)
+ self.client.add_vlans(ethernet, [vlan])
+ # add ipv4 layer
+ gw_ip = ipv4['gateway_ip'][core_tp_id]
+ # use gw addr to generate ip addr from the same network
+ ip_addr = ipaddress.IPv4Address(gw_ip) + 1
+ ipv4_obj = self.client.add_ipv4(ethernet, name='ipv4',
+ addr=ip_addr,
+ addr_step='0.0.0.1',
+ prefix=ipv4['prefix'][core_tp_id],
+ gateway=gw_ip)
+ self.protocols.append(ipv4_obj)
+ if ipv4.get("bgp"):
+ bgp_peer_obj = self.client.add_bgp(ipv4_obj,
+ dut_ip=ipv4["bgp"]["dut_ip"],
+ local_as=ipv4["bgp"]["as_number"],
+ bgp_type=ipv4["bgp"].get("bgp_type"))
+ self.protocols.append(bgp_peer_obj)
class IxiaRfc2544Helper(Rfc2544ResourceHelper):
@@ -41,6 +220,11 @@ class IxiaResourceHelper(ClientResourceHelper):
super(IxiaResourceHelper, self).__init__(setup_helper)
self.scenario_helper = setup_helper.scenario_helper
+ self._ixia_scenarios = {
+ "IxiaBasic": IxiaBasicScenario,
+ "IxiaPppoeClient": IxiaPppoeClientScenario,
+ }
+
self.client = ixnet_api.IxNextgen()
if rfc_helper_type is None:
@@ -49,6 +233,8 @@ class IxiaResourceHelper(ClientResourceHelper):
self.rfc_helper = rfc_helper_type(self.scenario_helper)
self.uplink_ports = None
self.downlink_ports = None
+ self.context_cfg = None
+ self._ix_scenario = None
self._connect()
def _connect(self, client=None):
@@ -57,7 +243,12 @@ class IxiaResourceHelper(ClientResourceHelper):
def get_stats(self, *args, **kwargs):
return self.client.get_statistics()
+ def setup(self):
+ super(IxiaResourceHelper, self).setup()
+ self._init_ix_scenario()
+
def stop_collect(self):
+ self._ix_scenario.stop_protocols()
self._terminated.value = 1
def generate_samples(self, ports, duration):
@@ -92,14 +283,24 @@ class IxiaResourceHelper(ClientResourceHelper):
return samples
+ def _init_ix_scenario(self):
+ ixia_config = self.scenario_helper.scenario_cfg.get('ixia_config', 'IxiaBasic')
+
+ if ixia_config in self._ixia_scenarios:
+ scenario_type = self._ixia_scenarios[ixia_config]
+
+ self._ix_scenario = scenario_type(self.client, self.context_cfg,
+ self.scenario_helper.scenario_cfg['options'])
+ else:
+ raise RuntimeError(
+ "IXIA config type '{}' not supported".format(ixia_config))
+
def _initialize_client(self):
"""Initialize the IXIA IxNetwork client and configure the server"""
self.client.clear_config()
self.client.assign_ports()
- vports = self.client.get_vports()
- uplink_vports = vports[::2]
- downlink_vports = vports[1::2]
- self.client.create_traffic_model(uplink_vports, downlink_vports)
+ self._ix_scenario.apply_config()
+ self._ix_scenario.create_traffic_model()
def run_traffic(self, traffic_profile, *args):
if self._terminated.value:
@@ -123,6 +324,8 @@ class IxiaResourceHelper(ClientResourceHelper):
mac["src_mac_{}".format(port_num)] = virt_intf.get("local_mac", default)
mac["dst_mac_{}".format(port_num)] = virt_intf.get("dst_mac", default)
+ self._ix_scenario.run_protocols()
+
try:
while not self._terminated.value:
first_run = traffic_profile.execute_traffic(
@@ -144,6 +347,7 @@ class IxiaResourceHelper(ClientResourceHelper):
except Exception: # pylint: disable=broad-except
LOG.exception('Run Traffic terminated')
+ self._ix_scenario.stop_protocols()
self._terminated.value = 1
def collect_kpi(self):