diff options
author | Dan Klein <danklein10@gmail.com> | 2015-10-06 07:28:24 +0300 |
---|---|---|
committer | Dan Klein <danklein10@gmail.com> | 2015-10-06 07:28:24 +0300 |
commit | 9629c9953516281d4bdaad1ed63d145de336a983 (patch) | |
tree | 5ae39833e1e436978911d0d6ffb77cfa265ba8f9 /scripts | |
parent | c7ba20f4f7c4521fdf50238c7e4ccc50f13b248e (diff) |
Commiting last night progress
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/automation/trex_control_plane/client/trex_stateless_client.py | 300 | ||||
-rwxr-xr-x | scripts/automation/trex_control_plane/client_utils/packet_builder.py | 118 |
2 files changed, 292 insertions, 126 deletions
diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 12f6eff6..b7580531 100644 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -16,135 +16,66 @@ class CTRexStatelessClient(object): def __init__(self, server="localhost", port=5050, virtual=False): super(CTRexStatelessClient, self).__init__() self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) + self._conn_handler = {} - def add_stream(self, handler, port_id, stream_id, stream_obj): - assert isinstance(stream_obj, CTRexStatelessClient.CStream) - params = {"handler":handler, + def owned(func): + def wrapper(self, *args, **kwargs ) : + if self._conn_handler.get(kwargs.get("port_id")): + return func(self, *args, **kwargs) + else: + raise RuntimeError("The requested method ('{0}') cannot be invoked unless the desired port is owned". + format(func.__name__)) + return wrapper + + def acquire(self, port_id, username, force=False): + params = {"port_id": port_id, + "user": username, + "force": force} + self._conn_handler[port_id] = self.transmit("acquire", params) + return self._conn_handler + + @owned + def release(self, port_id=None): + self._conn_handler.pop(port_id) + params = {"handler":self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("release", params) + + @owned + def add_stream(self, stream_id, stream_obj, port_id=None): + assert isinstance(stream_obj, CStream) + params = {"handler":self._conn_handler.get(port_id), "port_id":port_id, "stream_id":stream_id, "stream":stream_obj.dump()} return self.transmit("add_stream", params) - def transmit(self, method_name, params={}): - return self.tx_link.transmit(method_name, params) + @owned + def remove_stream(self, stream_id, port_id=None): + params = {"handler":self._conn_handler.get(port_id), + "port_id":port_id, + "stream_id":stream_id} + return self.transmit("remove_stream", params) + @owned + def get_stream_list(self, port_id=None): + params = {"handler":self._conn_handler.get(port_id), + "port_id":port_id} + return self.transmit("get_stream_list", params) - # ------ private classes ------ # - class CStream(object): - """docstring for CStream""" - def __init__(self): - super(CTRexStatelessClient.CStream, self).__init__() - self.packet = CTRexPktBuilder() - self.rx_stats = CTRexStatelessClient.CRxStats() - self.mode = CTRexStatelessClient.CTxMode() - self.isg - self._next_stream = -1 - self._self_start - self._enabled - - @property - def enabled(self): - return self._enabled - - @enabled.setter - def enabled(self, bool_value): - self._enabled = bool(bool_value) - - @property - def self_start(self): - return self._self_start - - @self_start.setter - def self_start(self, bool_value): - self._self_start = bool(bool_value) - - @property - def next_stream(self): - return self._next_stream - - @next_stream.setter - def next_stream(self, value): - self._next_stream = int(bool_value) - - def dump(self): - pass - return {"enabled":self.enabled, - "self_start":self.self_start, - "isg":self.isg, - "next_stream":self.next_stream, - "packet":self.packet.dump_pkt(), - "mode":self.mode.dump(), - "vm":self.packet.dump_vm_instructions(), # TODO - add this method to packet builder module - "rx_stats":self.rx_stats.dump()} - - class CRxStats(object): - - def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): - self._rx_dict = {"enabled" : enabled, - "seq_enabled" : seq_enabled, - "latency_enabled" : latency_enabled} - - @property - def enabled(self): - return self._rx_dict.get("enabled") - - @enabled.setter - def enabled(self, bool_value): - self._rx_dict['enabled'] = bool(bool_value) - - @property - def seq_enabled(self): - return self._rx_dict.get("seq_enabled") - - @seq_enabled.setter - def seq_enabled(self, bool_value): - self._rx_dict['seq_enabled'] = bool(bool_value) - - @property - def latency_enabled(self): - return self._rx_dict.get("latency_enabled") - - @latency_enabled.setter - def latency_enabled(self, bool_value): - self._rx_dict['latency_enabled'] = bool(bool_value) - - def dump(self): - return {k:v - for k,v in self._rx_dict.items() - if v - } - - class CTxMode(object): - """docstring for CTxMode""" - def __init__(self, tx_mode, pps): - super(CTxMode, self).__init__() - if tx_mode not in ["continuous", "single_burst", "multi_burst"]: - raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) - self._tx_mode = tx_mode - self._fields = {'pps':float(pps)} - if tx_mode == "single_burst": - self._fields['total_pkts'] = 0 - elif tx_mode == "multi_burst": - self._fields['pkts_per_burst'] = 0 - self._fields['ibg'] = 0.0 - self._fields['count'] = 0 - else: - pass + @owned + def get_stream(self, stream_id, port_id=None): + params = {"handler":self._conn_handler.get(port_id), + "port_id":port_id} + return self.transmit("get_stream_list", params) - def set_tx_mode_attr(self, attr, val): - if attr in self._fields: - self._fields[attr] = type(self._fields.get(attr))(val) - else: - raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". - format(attr, self._tx_mode)) - def dump(self): - dump = {"type":self._tx_mode} - dump.update({k:v - for k, v in self._fields.items() - }) - return dump + def transmit(self, method_name, params={}): + return self.tx_link.transmit(method_name, params) + + + # ------ private classes ------ # class CTxLink(object): """describes the connectivity of the stateless client method""" def __init__(self, server="localhost", port=5050, virtual=False): @@ -167,7 +98,140 @@ class CTRexStatelessClient(object): else: return self.rpc_link.invoke_rpc_method(method_name, params) +class CStream(object): + """docstring for CStream""" + DEFAULTS = {"rx_stats": CRxStats, + "mode": CTxMode, + "isg": 5.0, + "next_stream": -1, + "self_start": True, + "enabled": True} + + def __init__(self, **kwargs): + super(CStream, self).__init__() + for k, v in kwargs.items(): + setattr(self, k, v) + # set default values to unset attributes, according to DEFAULTS dict + set_keys = set(kwargs.keys()) + keys_to_set = [x for x in self.DEFAULTS if x not in set_keys] + for key in keys_to_set: + default = self.DEFAULTS.get(key) + if type(default)==type: + setattr(self, key, default()) + else: + setattr(self, key, default) + + @property + def packet(self): + return self._packet + + @packet.setter + def packet(self, packet_obj): + assert isinstance(packet_obj, CTRexPktBuilder) + self._packet = packet_obj + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, bool_value): + self._enabled = bool(bool_value) + + @property + def self_start(self): + return self._self_start + + @self_start.setter + def self_start(self, bool_value): + self._self_start = bool(bool_value) + + @property + def next_stream(self): + return self._next_stream + + @next_stream.setter + def next_stream(self, value): + self._next_stream = int(value) + + def dump(self): + pass + return {"enabled":self.enabled, + "self_start":self.self_start, + "isg":self.isg, + "next_stream":self.next_stream, + "packet":self.packet.dump_pkt(), + "mode":self.mode.dump(), + "vm":self.packet.get_vm_data(), + "rx_stats":self.rx_stats.dump()} + +class CRxStats(object): + + def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): + self._rx_dict = {"enabled" : enabled, + "seq_enabled" : seq_enabled, + "latency_enabled" : latency_enabled} + + @property + def enabled(self): + return self._rx_dict.get("enabled") + + @enabled.setter + def enabled(self, bool_value): + self._rx_dict['enabled'] = bool(bool_value) + + @property + def seq_enabled(self): + return self._rx_dict.get("seq_enabled") + + @seq_enabled.setter + def seq_enabled(self, bool_value): + self._rx_dict['seq_enabled'] = bool(bool_value) + + @property + def latency_enabled(self): + return self._rx_dict.get("latency_enabled") + + @latency_enabled.setter + def latency_enabled(self, bool_value): + self._rx_dict['latency_enabled'] = bool(bool_value) + + def dump(self): + return {k:v + for k,v in self._rx_dict.items() + if v + } + +class CTxMode(object): + """docstring for CTxMode""" + def __init__(self, tx_mode, pps): + super(CTxMode, self).__init__() + if tx_mode not in ["continuous", "single_burst", "multi_burst"]: + raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) + self._tx_mode = tx_mode + self._fields = {'pps':float(pps)} + if tx_mode == "single_burst": + self._fields['total_pkts'] = 0 + elif tx_mode == "multi_burst": + self._fields['pkts_per_burst'] = 0 + self._fields['ibg'] = 0.0 + self._fields['count'] = 0 + else: + pass + def set_tx_mode_attr(self, attr, val): + if attr in self._fields: + self._fields[attr] = type(self._fields.get(attr))(val) + else: + raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". + format(attr, self._tx_mode)) + + def dump(self): + dump = {"type":self._tx_mode} + dump.update({k:v + for k, v in self._fields.items() + }) + return dump if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index 30aede0b..0505d7f1 100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -64,6 +64,36 @@ class CTRexPktBuilder(object): return def set_ip_layer_addr(self, layer_name, attr, ip_addr, ip_type="ipv4"): + """ + This method sets the IP address fields of an IP header (source or destination, for both IPv4 and IPv6) + using a human readable addressing representation. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l3_ip", etc. + + attr: str + a string representation of the sub-field to be set: + + "src" for source + + "dst" for destination + + ip_addr: str + a string representation of the IP address to be set. + Example: "10.0.0.1" for IPv4, or "5001::DB8:1:3333:1:1" for IPv6 + + ip_type : str + a string representation of the IP version to be set: + + "ipv4" for IPv4 + + "ipv6" for IPv6 + + Default: **ipv4** + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an IP layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ try: layer = self._pkt_by_hdr[layer_name.lower()] if not (isinstance(layer, dpkt.ip.IP) or isinstance(layer, dpkt.ip6.IP6)): @@ -75,9 +105,54 @@ class CTRexPktBuilder(object): raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) def set_ipv6_layer_addr(self, layer_name, attr, ip_addr): + """ + This method sets the IPv6 address fields of an IP header (source or destination) + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l3_ip", etc. + + attr: str + a string representation of the sub-field to be set: + + "src" for source + + "dst" for destination + + ip_addr: str + a string representation of the IP address to be set. + Example: "5001::DB8:1:3333:1:1" + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an IPv6 layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ self.set_ip_layer_addr(layer_name, attr, ip_addr, ip_type="ipv6") def set_eth_layer_addr(self, layer_name, attr, mac_addr): + """ + This method sets the ethernet address fields of an Ethernet header (source or destination) + using a human readable addressing representation. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l2", etc. + + attr: str + a string representation of the sub-field to be set: + + "src" for source + + "dst" for destination + + mac_addr: str + a string representation of the MAC address to be set. + Example: "00:de:34:ef:2e:f4". + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an Ethernet layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ try: layer = self._pkt_by_hdr[layer_name.lower()] if not isinstance(layer, dpkt.ethernet.Ethernet): @@ -136,6 +211,29 @@ class CTRexPktBuilder(object): raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) def set_layer_bit_attr(self, layer_name, attr, val): + """ + This method enables the user to set the value of a field smaller that 1 Byte in size. + This method isn't used to set full-sized fields value (>= 1 byte). + Use :func:`packet_builder.CTRexPktBuilder.set_layer_attr` instead. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l2", "l4_tcp", etc. + + attr : str + a string representing the attribute to be set on desired layer + + val : int + value of attribute. + This value will be set "ontop" of the existing value using bitwise "OR" operation. + .. tip:: It is very useful to use dpkt constants to define the values of these fields. + + :raises: + + :exc:`KeyError`, in case of missing layer (the desired layer isn't part of packet) + + :exc:`ValueError`, in case invalid attribute to the specified layer. + + """ return self.set_layer_attr(layer_name, attr, val, True) def set_pkt_payload(self, payload): @@ -241,7 +339,7 @@ class CTRexPktBuilder(object): # VM access methods def set_vm_ip_range(self, ip_layer_name, ip_field, ip_init, ip_start, ip_end, add_value, - operation, is_big_endian=False, val_size = 4, + operation, is_big_endian=False, val_size=4, ip_type="ipv4", add_checksum_inst=True): if ip_field not in ["src", "dst"]: raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses") @@ -275,7 +373,7 @@ class CTRexPktBuilder(object): def set_vm_eth_range(self, eth_layer_name, eth_field, mac_init, mac_start, mac_end, add_value, - operation, val_size = 4, is_big_endian=False): + operation, val_size=4, is_big_endian=False): if eth_field not in ["src", "dst"]: raise ValueError("set_vm_eth_range only available for source ('src') or destination ('dst') eth addresses") self._verify_layer_prop(eth_layer_name, dpkt.ethernet.Ethernet) @@ -299,15 +397,20 @@ class CTRexPktBuilder(object): init_val, start_val, end_val, add_val, val_size, operation, is_big_endian=False, range_name="", add_checksum_inst=True): + # verify input validity for init/start/end values + for val in [init_val, start_val, end_val]: + if not isinstance(val, int): + raise ValueError("init/start/end values are expected integers, but received type '{0}'". + format(type(val))) self._verify_layer_prop(layer_name=layer_name, field_name=hdr_field) if not range_name: range_name = "{layer}__{field}".format(layer=layer_name, field=hdr_field) trim_size = val_size*2 hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field, val_size) self.vm.add_flow_man_inst(range_name, size=val_size, operation=operation, - init_value=init_val, - min_value=start_val, - max_value=end_val) + init_value=str(init_val), + min_value=str(start_val), + max_value=str(end_val)) self.vm.add_write_flow_inst(range_name, field_abs_offset) self.vm.set_vm_off_inst_field(range_name, "add_value", add_val) self.vm.set_vm_off_inst_field(range_name, "is_big_endian", is_big_endian) @@ -315,7 +418,7 @@ class CTRexPktBuilder(object): self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(layer_name), hdr_offset) def get_vm_data(self): - pass + return self.vm.dump() def dump_pkt(self): """ @@ -369,8 +472,7 @@ class CTRexPktBuilder(object): except IOError: raise IOError(2, "The provided path could not be accessed") - # ----- useful shortcut methods ----- # - def gen_dns_packet(self): + def export_pkt(self, file_path, link_pcap=False, pcap_name=None, pcap_ts=None): pass # ----- internal methods ----- # |