diff options
Diffstat (limited to 'yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py')
-rw-r--r-- | yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py | 351 |
1 files changed, 321 insertions, 30 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 d4f75babb..89a855480 100644 --- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py +++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2018 Intel Corporation +# Copyright (c) 2016-2019 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,6 +14,8 @@ import ipaddress import logging +import re +import collections import IxNetwork @@ -41,14 +43,34 @@ C_VLAN = 1 ETHER_TYPE_802_1ad = '0x88a8' -IP_VERSION_4_MASK = 24 -IP_VERSION_6_MASK = 64 - TRAFFIC_STATUS_STARTED = 'started' TRAFFIC_STATUS_STOPPED = 'stopped' +PROTOCOL_STATUS_UP = 'up' +PROTOCOL_STATUS_DOWN = ['down', 'notStarted'] + SUPPORTED_PROTO = [PROTO_UDP] +SUPPORTED_DSCP_CLASSES = [ + 'defaultPHB', + 'classSelectorPHB', + 'assuredForwardingPHB', + 'expeditedForwardingPHB'] + +SUPPORTED_TOS_FIELDS = [ + 'precedence', + 'delay', + 'throughput', + 'reliability' +] + +IP_PRIORITY_PATTERN = r'[^\w+]*.+(Raw priority|' \ + 'Precedence|' \ + 'Default PHB|' \ + 'Class selector PHB|' \ + 'Assured forwarding selector PHB|' \ + 'Expedited forwarding PHB)' + class Vlan(object): def __init__(self, @@ -69,14 +91,11 @@ class IxNextgen(object): # pragma: no cover PORT_STATS_NAME_MAP = { "stat_name": 'Stat Name', + "port_name": 'Port Name', "Frames_Tx": 'Frames Tx.', "Valid_Frames_Rx": 'Valid Frames Rx.', - "Frames_Tx_Rate": 'Frames Tx. Rate', - "Valid_Frames_Rx_Rate": 'Valid Frames Rx. Rate', - "Tx_Rate_Kbps": 'Tx. Rate (Kbps)', - "Rx_Rate_Kbps": 'Rx. Rate (Kbps)', - "Tx_Rate_Mbps": 'Tx. Rate (Mbps)', - "Rx_Rate_Mbps": 'Rx. Rate (Mbps)', + "Bytes_Tx": 'Bytes Tx.', + "Bytes_Rx": 'Bytes Rx.' } LATENCY_NAME_MAP = { @@ -85,6 +104,42 @@ class IxNextgen(object): # pragma: no cover "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)', } + FLOWS_STATS_NAME_MAP = { + "Tx_Port": 'Tx Port', + "VLAN-ID": 'VLAN:VLAN-ID', + "IP_Priority": re.compile(IP_PRIORITY_PATTERN), + "Flow_Group": 'Flow Group', + "Tx_Frames": 'Tx Frames', + "Rx_Frames": 'Rx Frames', + "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)', + "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)', + "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)' + } + + PPPOX_CLIENT_PER_PORT_NAME_MAP = { + 'subs_port': 'Port', + 'Sessions_Up': 'Sessions Up', + 'Sessions_Down': 'Sessions Down', + 'Sessions_Not_Started': 'Sessions Not Started', + 'Sessions_Total': 'Sessions Total' + } + + PORT_STATISTICS = '::ixNet::OBJ-/statistics/view:"Port Statistics"' + FLOW_STATISTICS = '::ixNet::OBJ-/statistics/view:"Flow Statistics"' + PPPOX_CLIENT_PER_PORT = '::ixNet::OBJ-/statistics/view:"PPPoX Client Per Port"' + + PPPOE_SCENARIO_STATS = { + 'port_statistics': PORT_STATISTICS, + 'flow_statistic': FLOW_STATISTICS, + 'pppox_client_per_port': PPPOX_CLIENT_PER_PORT + } + + PPPOE_SCENARIO_STATS_MAP = { + 'port_statistics': PORT_STATS_NAME_MAP, + 'flow_statistic': FLOWS_STATS_NAME_MAP, + 'pppox_client_per_port': PPPOX_CLIENT_PER_PORT_NAME_MAP + } + @staticmethod def get_config(tg_cfg): card = [] @@ -126,6 +181,9 @@ class IxNextgen(object): # pragma: no cover vports = self.ixnet.getList(self.ixnet.getRoot(), 'vport') return vports + def get_static_interface(self, vport): + return self.ixnet.getList(vport, 'interface') + def _get_config_element_by_flow_group_name(self, flow_group_name): """Get a config element using the flow group name @@ -180,6 +238,24 @@ class IxNextgen(object): # pragma: no cover return self.ixnet.getAttribute(self.ixnet.getRoot() + 'traffic', '-state') + def _get_protocol_status(self, proto): + """Get protocol status + + :param proto: IxNet protocol str representation, e.g.: + '::ixNet::OBJ-/topology:2/deviceGroup:1/ethernet:1/ipv4:L14' + :return: (list) protocol status: list of sessions protocol + statuses which include states 'up', 'down' and 'notStarted' + """ + return self.ixnet.getAttribute(proto, '-sessionStatus') + + def get_topology_device_groups(self, topology): + """Get list of device groups in topology + + :param topology: (str) topology descriptor + :return: (list) list of device groups descriptors + """ + return self.ixnet.getList(topology, 'deviceGroup') + def is_traffic_running(self): """Returns true if traffic state == TRAFFIC_STATUS_STARTED""" return self._get_traffic_state() == TRAFFIC_STATUS_STARTED @@ -188,6 +264,28 @@ class IxNextgen(object): # pragma: no cover """Returns true if traffic state == TRAFFIC_STATUS_STOPPED""" return self._get_traffic_state() == TRAFFIC_STATUS_STOPPED + def is_protocols_running(self, protocols): + """Returns true if all protocols statuses are PROTOCOL_STATUS_UP + + :param protocols: list of protocols str representations, e.g.: + ['::ixNet::OBJ-/topology:2/deviceGroup:1/ethernet:1/ipv4:L14', ...] + :return: (bool) True if all protocols status is 'up', False if any + protocol status is 'down' or 'notStarted' + """ + 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 + + :param protocols: list of protocols str representations, e.g.: + ['::ixNet::OBJ-/topology:2/deviceGroup:1/ethernet:1/ipv4:L14', ...] + :return: (bool) True if all protocols status is 'down' or 'notStarted', + False if any protocol status is 'up' + """ + 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): """Parse "framesize" config param. to return a list of weighted pairs @@ -303,7 +401,25 @@ class IxNextgen(object): # pragma: no cover '/traffic/protocolTemplate:"{}"'.format(protocol_name)) self.ixnet.execute('append', previous_element, protocol) - def _setup_config_elements(self, add_default_proto=True): + def is_qinq(self, flow_data): + for traffic_type in flow_data: + if flow_data[traffic_type]['outer_l2'].get('QinQ'): + return True + return False + + def _flows_settings(self, cfg): + flows_data = [] + res = [key for key in cfg.keys() if key.split('_')[0] in ['uplink', 'downlink']] + for i in range(len(res)): + uplink = 'uplink_{}'.format(i) + downlink = 'downlink_{}'.format(i) + if uplink in res: + flows_data.append(cfg[uplink]) + if downlink in res: + flows_data.append(cfg[downlink]) + return flows_data + + def _setup_config_elements(self, traffic_profile, add_default_proto=True): """Setup the config elements The traffic item is configured to allow individual configurations per @@ -319,7 +435,9 @@ class IxNextgen(object): # pragma: no cover 'trafficItem')[0] log.info('Split the frame rate distribution per config element') config_elements = self.ixnet.getList(traffic_item_id, 'configElement') - for config_element in config_elements: + flows = self._flows_settings(traffic_profile.params) + # TODO: check length of both lists, it should be equal!!! + for config_element, flow_data in zip(config_elements, flows): self.ixnet.setAttribute(config_element + '/frameRateDistribution', '-portDistribution', 'splitRateEvenly') self.ixnet.setAttribute(config_element + '/frameRateDistribution', @@ -330,8 +448,13 @@ class IxNextgen(object): # pragma: no cover PROTO_UDP, config_element + '/stack:"ethernet-1"') self._append_procotol_to_stack( PROTO_IPV4, config_element + '/stack:"ethernet-1"') + if self.is_qinq(flow_data): + self._append_procotol_to_stack( + PROTO_VLAN, config_element + '/stack:"ethernet-1"') + self._append_procotol_to_stack( + PROTO_VLAN, config_element + '/stack:"ethernet-1"') - def create_traffic_model(self, uplink_ports, downlink_ports): + def create_traffic_model(self, uplink_ports, downlink_ports, traffic_profile): """Create a traffic item and the needed flow groups Each flow group inside the traffic item (only one is present) @@ -346,9 +469,10 @@ class IxNextgen(object): # pragma: no cover uplink_endpoints = [port + '/protocols' for port in uplink_ports] downlink_endpoints = [port + '/protocols' for port in downlink_ports] self._create_flow_groups(uplink_endpoints, downlink_endpoints) - self._setup_config_elements() + self._setup_config_elements(traffic_profile=traffic_profile) - def create_ipv4_traffic_model(self, uplink_topologies, downlink_topologies): + def create_ipv4_traffic_model(self, uplink_endpoints, downlink_endpoints, + traffic_profile): """Create a traffic item and the needed flow groups Each flow group inside the traffic item (only one is present) @@ -360,8 +484,9 @@ class IxNextgen(object): # pragma: no cover FlowGroup4: uplink2 <- downlink2 """ self._create_traffic_item('ipv4') - self._create_flow_groups(uplink_topologies, downlink_topologies) - self._setup_config_elements(False) + self._create_flow_groups(uplink_endpoints, downlink_endpoints) + self._setup_config_elements(traffic_profile=traffic_profile, + add_default_proto=False) def _update_frame_mac(self, ethernet_descriptor, field, mac_address): """Set the MAC address in a config element stack Ethernet field @@ -428,11 +553,6 @@ class IxNextgen(object): # pragma: no cover '-fieldValue', ETHER_TYPE_802_1ad, '-valueType', SINGLE_VALUE) - self._append_procotol_to_stack( - PROTO_VLAN, config_element + '/stack:"ethernet-1"') - self._append_procotol_to_stack( - PROTO_VLAN, config_element + '/stack:"ethernet-1"') - self._update_vlan_tag(fg_id, s_vlan, S_VLAN) self._update_vlan_tag(fg_id, c_vlan, C_VLAN) @@ -526,9 +646,11 @@ class IxNextgen(object): # pragma: no cover srcseed = traffic_param['outer_l3']['srcseed'] dstseed = traffic_param['outer_l3']['dstseed'] srcmask = traffic_param['outer_l3']['srcmask'] \ - or IP_VERSION_4_MASK + or ipaddress.IPV4LENGTH dstmask = traffic_param['outer_l3']['dstmask'] \ - or IP_VERSION_4_MASK + or ipaddress.IPV4LENGTH + priority = traffic_param['outer_l3']['priority'] + if srcip: self._update_ipv4_address( self._get_stack_item(fg_id, PROTO_IPV4)[0], @@ -537,6 +659,58 @@ class IxNextgen(object): # pragma: no cover self._update_ipv4_address( self._get_stack_item(fg_id, PROTO_IPV4)[0], 'dstIp', str(dstip), dstseed, dstmask, count) + if priority: + self._update_ipv4_priority( + self._get_stack_item(fg_id, PROTO_IPV4)[0], priority) + + def _update_ipv4_priority(self, ip_descriptor, priority): + """Set the IPv4 priority in a config element stack IP field + + :param ip_descriptor: (str) IP descriptor, e.g.: + /traffic/trafficItem:1/configElement:1/stack:"ipv4-2" + :param priority: (dict) priority configuration from traffic profile, e.g.: + {'tos': + 'precedence': [1, 4, 7] + } + """ + 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 priority.get('dscp'): + for field, value in priority['dscp'].items(): + 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 priority.get('tos'): + for field, value in priority['tos'].items(): + 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 + + :param field_descriptor: (str) field descriptor, e.g.: + /traffic/trafficItem:1/configElement:1/stack:"ipv4-2"/ \ + field:"ipv4.header.priority.raw-3 + :param value: (list, int) list of integers or single integer value + """ + if isinstance(value, list): + self.ixnet.setMultiAttribute(field_descriptor, + '-valueList', value, + '-activeFieldChoice', 'true', + '-valueType', 'valueList') + else: + self.ixnet.setMultiAttribute(field_descriptor, + '-activeFieldChoice', 'true', + '-singleValue', str(value)) + self.ixnet.commit() def update_l4(self, traffic): """Update the L4 headers @@ -608,6 +782,52 @@ class IxNextgen(object): # pragma: no cover 'getColumnValues', view_obj, data_ixia) for data_yardstick, data_ixia in name_map.items()} + def _get_view_page_stats(self, view_obj): + """Get full view page stats + + :param view_obj: view object, e.g. + '::ixNet::OBJ-/statistics/view:"Port Statistics"' + :return: (list) List of dicts. Each dict represents view page row + """ + view = view_obj + '/page' + column_headers = self.ixnet.getAttribute(view, '-columnCaptions') + view_rows = self.ixnet.getAttribute(view, '-rowValues') + view_page = [dict(zip(column_headers, row[0])) for row in view_rows] + return view_page + + def _set_egress_flow_tracking(self, encapsulation, offset): + """Set egress flow tracking options + + :param encapsulation: encapsulation type + :type encapsulation: str, e.g. 'Ethernet' + :param offset: offset type + :type offset: str, e.g. 'IPv4 TOS Precedence (3 bits)' + """ + traffic_item = self.ixnet.getList(self.ixnet.getRoot() + '/traffic', + 'trafficItem')[0] + # Enable Egress Tracking + self.ixnet.setAttribute(traffic_item, '-egressEnabled', True) + self.ixnet.commit() + + # Set encapsulation type + enc_obj = self.ixnet.getList(traffic_item, 'egressTracking')[0] + self.ixnet.setAttribute(enc_obj, '-encapsulation', encapsulation) + + # Set offset + self.ixnet.setAttribute(enc_obj, '-offset', offset) + self.ixnet.commit() + + def set_flow_tracking(self, track_by): + """Set flow tracking options + + :param track_by: list of tracking fields + :type track_by: list, e.g. ['vlanVlanId0','ipv4Precedence0'] + """ + traffic_item = self.ixnet.getList(self.ixnet.getRoot() + '/traffic', + 'trafficItem')[0] + self.ixnet.setAttribute(traffic_item + '/tracking', '-trackBy', track_by) + self.ixnet.commit() + def get_statistics(self): """Retrieve port and flow statistics @@ -617,14 +837,47 @@ class IxNextgen(object): # pragma: no cover :return: dictionary with the statistics; the keys of this dictionary are PORT_STATS_NAME_MAP and LATENCY_NAME_MAP keys. """ - port_statistics = '::ixNet::OBJ-/statistics/view:"Port Statistics"' - flow_statistics = '::ixNet::OBJ-/statistics/view:"Flow Statistics"' - stats = self._build_stats_map(port_statistics, + stats = self._build_stats_map(self.PORT_STATISTICS, self.PORT_STATS_NAME_MAP) - stats.update(self._build_stats_map(flow_statistics, - self.LATENCY_NAME_MAP)) + stats.update(self._build_stats_map(self.FLOW_STATISTICS, + self.LATENCY_NAME_MAP)) return stats + def get_pppoe_scenario_statistics(self): + """Retrieve port, flow and PPPoE subscribers statistics""" + stats = collections.defaultdict(list) + result = collections.defaultdict(list) + for stat, view in self.PPPOE_SCENARIO_STATS.items(): + # Get view total pages number + total_pages = self.ixnet.getAttribute( + view + '/page', '-totalPages') + # Collect stats from all view pages + for page in range(1, int(total_pages) + 1): + current_page = int(self.ixnet.getAttribute( + view + '/page', '-currentPage')) + if page != int(current_page): + self.ixnet.setAttribute(view + '/page', '-currentPage', + str(page)) + self.ixnet.commit() + page_data = self._get_view_page_stats(view) + stats[stat].extend(page_data) + # Filter collected views stats + for stat in stats: + for view_row in stats[stat]: + filtered_row = {} + for key, value in self.PPPOE_SCENARIO_STATS_MAP[stat].items(): + if isinstance(value, str): + filtered_row.update({key: view_row[value]}) + # Handle keys which values are represented by regex + else: + for k in view_row.keys(): + if value.match(k): + value = value.match(k).group() + filtered_row.update({key: view_row[value]}) + break + result[stat].append(filtered_row) + return result + def start_protocols(self): self.ixnet.execute('startAllProtocols') @@ -791,7 +1044,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) @@ -811,6 +1064,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 @@ -839,3 +1096,37 @@ class IxNextgen(object): # pragma: no cover '-value', bgp_type) self.ixnet.commit() return obj + + def add_interface(self, vport, ip, mac=None, gateway=None): + """Add protocol interface to the vport""" + log.debug("add_interface: mac='%s', ip='%s', gateway='%s'", mac, ip, + gateway) + obj = self.ixnet.add(vport, 'interface') + self.ixnet.commit() + + if mac is not None: + self.ixnet.setMultiAttribute(obj + '/ethernet', '-macAddress', mac) + + ipv4 = self.ixnet.add(obj, 'ipv4') + self.ixnet.setMultiAttribute(ipv4, '-ip', ip) + + if gateway is not None: + self.ixnet.setMultiAttribute(ipv4, '-gateway', gateway) + + self.ixnet.commit() + + self.ixnet.setMultiAttribute(obj, '-enabled', 'true') + self.ixnet.commit() + + return obj + + def add_static_ipv4(self, iface, vport, start_ip, count, mask='24'): + """Add static IP range to the interface""" + log.debug("add_static_ipv4: start_ip:'%s', count:'%s'", + start_ip, count) + obj = self.ixnet.add(vport + '/protocols/static', 'ip') + + self.ixnet.setMultiAttribute(obj, '-protocolInterface', iface, + '-ipStart', start_ip, '-count', count, + '-mask', mask, '-enabled', 'true') + self.ixnet.commit() |