The second version of our Tcl API brings important new features and a more consistent experience to the user.
This page explains the most fundamental and the rationale behind them. See our migration guide for more practical information on how to port older scripts to the new API v2.
Bringing an API to a new version and breaking full backwards compatibility is not a decision taken overnight. Let us describes some of the key issues with API 1.x that led to this decision, how the new API design alleviates these issues and which parts of the API needed fundamental refactoring to achieve this.
The ByteBlower Tcl API is available from the very start of ByteBlower as a commercial product. Over the years, a ton of features have been added, while maximum backwards compatibility was maintained.
Several structural constraints kept the product from evolving and made rolling out new functionality hard. The most important of these are:
Frame
type, even though nearly all of these frames have a fixed size. Since key parts of the server (network stack, handling of user events, ...) had to be rewritten to make our platform ready for the 10 Gigabit era and to make the long-awaited ByteBlower GUI 2.0 possible, the case for a new API was strong.
Furthermore, with compatibility constraints gone (even though we maintain compatibility calls where possible), some other aspects of the API could be cleaned up as well:
Tx.Stream
to the Frame
type. Aside from being the place where they belong, this allows us to roll out other frame modifiers in future. For example, a content modifier could create packets with varying UDP port numbers. CongestionWindowDowngrades.Get
, FinalCongestionWindowSize.Get
, MinimumWindowSize.Get
, RcvWindowScale.Get
and WindowScaling.Get
. The first key design choice is Objects over lists. Places that expected structured lists as arguments (e.g. parts of the IGMP protocol) or returned complex structured lists (e.g. the extended stream result) have been refactored.
The second element states that every component references its own results. For example, the result for a stream no longer contains per-frame results. Instead, these results accessible from the frame object itself. This makes sure the user does not have to parse complex structures to find relevant information, but simple asks the data he actually needs!
In the 1.x API, all known session parameters/counters where stored in a HTTPSessionInfoObject. This caused that the distinction between parameters/counter on TCP layer and parameters/counters on the HTTP Layer was unclear.
Since we've redesigned our TCP stack we had the opportunity to make things more clear and to split TCP and HTTP up into 2 SessionInfo objects. Our stack keeps also a lot more different usefull counters than those that were exposed to the user e.g. MIN RTT/Avg.RTT/MaxRTT. Those new counters help you to identify TCP/HTTP issues in your DUT.
In the 1.x API all counters where returned as a TCL-list. These lists where name-value pairs. In order to retrieve the counter you wanted, you had to use lindex and start counting the number of elements. Using TCL-Lists forced us to keep the order of the counters. Adding a new counter was only possible by putting it at the end of the list. At the beginning of our commercial API we had small lists but as ByteBlower evolved, the lists grow. We've desided to stop using TCL-lists as it isn't maintainable and impossible to introduce our new feature of Result Histories in the 2.x.
The new design for the results is based upon a proven concept in ByteBlower: Objects. A Rx.Trigger now has a Result-Object. On this object you can request directly the counter-value you want e.g. PacketCount.Get without the need of parsing. This makes your tcl-code readable for your colleagues.
API 1.x
API 2.x
One of the most requested features by you was some results over time.
We've managed to keep the old Counters.Get method but marked it as deprecated. So you still can use the old call.
In the 1.x API, frame modifiers are applied to streams instead of specific frames.
Configuring frame modifiers on a stream is not only counter-intuitive but has some downsides as well.
First of all, the old server maintained a TX size distribution table (for framesizes from 60 up to 10000 bytes) for each individual frame of each flow. This was needed, since the extended result contains this distribution for each individual frame on the server. Note that, unless a size modifier was applied to the stream, these distribution tables contained only one non-zero element. To make our server ready for 10 Gigabit processing, this was no longer an option.
Most of the inconsistencies were already solved by moving to result object: each object contains a reference to its own results. Note that this is incompatible with a frame modifier Currently, there is an inconsistency in the API within the following elements:
The framesize modifier is in essence a frame modifier, not a stream modifier. Applying a framesize modifier on a Tx.Stream actually overrides the size of each of the (current or future) frames in that stream. A lot happens in the background here. This is also shown in the TX size distribution (the result matching the framesize modifier) which is provided at the frame level. Intuitively, a Stream is a traffic structure: containing (a mix of) frames; specifying a frame rate, burstiness, etc. We use the Tx.Stream object to obtain (aggregate) results about the stream, as well as its individual frames. Notice that most scripts have references to Frame objects, which are always part of a single stream. But when retrieving the result we have to match results with these frames through the stream. A TX size distribution only makes sense if we apply a framesize modifier on a Frame. Therefore it should only be activated when such modifier exists... ... and the result should be linked to that modifier. Differences between Tx (stream) and Rx resulting (triggers, latency,...) should be avoided. The current main differences are: Tx.Stream currently has 2 types of result (basic and extended), where as an Rx.<...> has only one. Tx.Stream result types contain information about both the stream as a whole and the individual frames.
In the new API, frame modifiers are moved from the stream object to the frame object.
The current stream-based methods are deprecated, but are implemented for backwards compatibility. See their documentation for more information:
Tx.Stream::Modifier.Frame.Set
FrameModifier.RandomSize
and FrameModifier.GrowingSize
Tx.Stream::Counters.SizeDistribution.Get
In situations where a size modifier is applied to a stream with multiple frames (using the deprecated methods), it will now be applied to every individual frame instead of the stream as a whole. For growing size modifiers, this means the actual packets leaving the server may be different.
Consider an example where a stream has 2 frames A (with UDP destination 2000) and B (with UDP destination 4000) and a growing modifier from 60 bytes to 100 bytes. In the old situation, the packets leaving the server would be
A-60 B-61 A-62 B-63 A-64 B-65 A-66 ...
where as the new situation would result in
A-60 B-60 A-61 B-61 A-62 B-62 A-63 ...
Over the years, a lot of methods that either require or return timing values have been added. The scalar value they return must be interpreted in a variety of ways, which is only clear from the documentation. As of API v2, all values are specified in nanoseconds. If some method requires a value of a smaller resolution (e.g. milliseconds) and an invalid value is passed, a clear exception is thrown.
IGMP and MLD got a re-design in method namings. Due to historical reasons the API of both protocols was polluted. With the release of 2.x API we grabed the chance to change this and make the API consitend over both the protocols.