High-Level API
HL-API uses the classes defined in LL-API and lets you quickly develop scripts or program in an object-oriented fashion with explicit definition of commands of different tester, module, port types. In addition, the HL-API layer provides functionalities such as:
API Notation and Namings
HL-API aims to be semantic in function naming to avoid expectation conflict, as well as avoiding methods that can return values of different types. The key rule is: one method, one action. The following notations are used throughout this chapter.
<resource>
:Represents
Tester | Module | Port | <indices> | <namespace_class>
.<indices>
:Represents stream indices, connection group indices, filter indices, etc.
<namespace_class>
:A group of commands that manage the resources of the same kind but still stays at the same level as others.
<command_oo_name>
:command name adapted to the object-oriented programming concept. Commands of the same access level, which read or modify parameters of the same type, are grouped under one
<namespace_class>
.
An example of HL-API notation and namings based on the corresponding XOA CLI command names:
P_SPEEDSELECTION
P_SPEEDS_SUPPORTED
are represented as
<resource>.speed.selection
<resource>.speed.supported
Note
If there is a method returning both a single value and multiple values, it is considered a bug.
Get and Set Methods
There are only two types of methods for each command, get
and/or set
:
Method
get
is used to query the values, status, configuration of the resource.
When you do get
, you will receive an object that contains the values as its properties. For example, if you want to get the IPv4 configuration of a port, you should do resp = await port.net_config.ipv4.address.get()
and then variable resp
will have all the values returned by P_IPADDRESS
. You can then type .
after the resp
and your IDE will show the attributes for you to select as shown below.
Method
set
is used to change the values, status, configuration of the resource.
When you do set
, your IDE will automatically pop up the expected input arguments and their types as shown below.
Attention
To use get
and set
methods, you need to use await
because they are all made asynchronous.
Syntax:
resp = await <resource>.<command_oo_name>.get()
await <resource>.<command_oo_name>.set(<values>)
await <resource>.<command_oo_name>.set_<variation_name>()
await <resource>.<command_oo_name>.set_<variation_name>(<extra_value>)
Example:
resp = await <Port>.speed.supported.get()
await <Port>.speed.selection.set(mode=PortSpeedMode.AUTO)
await <Port>.<resource>.speed.selection.set_auto()
await <Stream>.packet.length.set_incrementing(min_val=100, max_val=500)
See also
Event Subscription and Push Notification
Periodical querying of test resource information, such as port sync statue, is low in communication efficiency and less responsive. Different from XOA CLI, HL-API supports push notification sent from the chassis server when the state or the configuration of a test resource changes. For instance, when a port starts generating traffic, its traffic state is changed from off to on, thus all the connected client programs will receive a push notification message of the new state from the chassis server.
HL-API provides functions for you to subscribe to events, which are triggered test resource state/configuration changes. Thus, your script/application can catch the push notifications and act accordingly.
Syntax:
<resource>.on_<command_oo_name>_change(<async_callback_function>)
Example:
port.on_traffic_change(my_calllback_function)
import asyncio
async def my_calllback_function(port, new_value)
...
Important
The <async_callback_function>
must be a coroutine function
Parameters that are passed to your <async_callback_function>
depend on the resource it is affiliated:
Under the Tester level:
<ref_tester>, <new_value>
Under the Module level:
<ref_module>, <new_value>
Under the Port level:
<ref_port>, <new_value>
Attention
Exception to the rule above is the event on_disconnected
. The parameters passed to it are tuple(<tester_ip: str>, <tester_port: int>)
Note
A subscription to an event only provides a tool for notifying the external code. It is unnecessary to update the library instance state manually, because it is automatically handled by the library code.
It is allowed to subscribe multiple callback functions to one event.
Resource Managers
Most of the subtester resources, which are organized into collections, are handled by Resource Managers.
The most commonly used resource managers are Module Manager and Port Manager | Index Managers.
An illustration of resource managers and test resources are shown below:
------------------
| Tester |
------------------
|
*******************
| module manager |
*******************
|
| --------------
|---| Module 0 |
| --------------
| |
| *******************
| | port manager |
| *******************
| |
| | -------------- ******************
| |----| Port 0 | - | index managers |
| | -------------- ******************
| | -------------- ******************
| |----| Port 1 | - | index managers |
| | -------------- ******************
| | -------------- ******************
| |----| Port N-1 | - | index managers |
| -------------- ******************
|
| --------------
|---| Module 1 |
| --------------
| |
| *******************
| | port manager |
| *******************
| |
| | -------------- ******************
| |----| Port 0 | - | index managers |
| | -------------- ******************
| | -------------- ******************
| |----| Port 1 | - | index managers |
| | -------------- ******************
| | -------------- ******************
| |----| Port N-1 | - | index managers |
| -------------- ******************
|
| --------------
|---| Module N-1 |
--------------
Note
Each resource manager is an iterable object
Module Manager and Port Manager
Each tester object contains a Module Manager, which can be accessed through attribute modules
, e.g. my_tester.modules
.
Each module object contains a Port Manager, which can be accessed through attribute ports
, e.g. my_module.ports
.
Important
Modules and ports are test resources that cannot be created or deleted, unless the tester is reconfigured either physically or virtually. Thus, in XOA Python API, there is no “create” or “delete” methods for these two types of objects. What we can do is to obtain the object that represents the underlying test resource.
A Module Manager can contain modules of different Module Types. This is because there can be various test modules installed in a physical tester. On the other hand, a Port Manager contains ports of the same Port Type. This is because the ports on a module are of the same type.
Attention
obtain()
is not a coroutine function, so don’t use await
with it.
Gain Access to Single Object
Methods to gain access to a module or a port from a resource manager:
Syntax:
obtain(<module-index> | <port-index>)
Gain Access to Multiple Objects
Methods to gain access to multiple resources from a resource manager:
Syntax:
obtain_multiple(<module-index> | <port-index>, ...)
Index Managers
Each port object contains several Index Managers that manage the subport-level resource indices such as stream indices, filter indices, connection group indices, modifier indices, etc. It automatically ensures correct and conflict-free index assignment.
For L23:
Stream Index Manager can be accessed through attribute
streams
, e.g.my_l23_port.streams
.Filter Index Manager can be accessed through attribute
filters
, e.g.my_l23_port.filters
.Match Term Index Manager can be accessed through attribute
match_terms
, e.g.my_l23_port.match_terms
.Length Term Index Manager can be accessed through attribute
length_terms
, e.g.my_l23_port.length_terms
.Histogram Dataset Index Manager can be accessed through attribute
datasets
, e.g.my_l23_port.datasets
.Modifier Index Manager can be accessed through attribute
modifiers
underpacket.header
of a stream object, e.g.my_stream.packet.header.modifiers
For L47:
Connection Group Index Manager can be accessed through attribute
streams
, e.g.my_l47_port.connection_groups
.
Important
Streams, connection groups, filters, modifiers, etc. are virtual. They can be created and deleted. Thus in XOA Python API, there are create, delete, and remove methods for you to manage these virtual resources.
It is user’s responsibility to create, retrieve, and delete those subport-level indices. Index Managers only takes care of the index assignment.
When you create an index instance under a port, e.g. a stream, the Stream Index Manager will pick an available value and assign it to the stream as the stream index. When you delete an index instance, the index manager will mark that index value as available. When you create an index instance again, the index manager will take the freed values first instead of creating a new one. This makes sure when the index manager cannot create more index instances is only because of the port capability, not because of the wasted index values.
Thanks to the index assignment mechanism, you don’t necessarily need to handle the index assignment but concentrating on the test logic. Methods to manage subport-level instances:
To create an index, use the method
<index_manager>.create()
under the index manager, e.g.my_stream = await my_port.streams.create()
.To delete an index, you can use the method
<index_manager>.remove(<index>)
under the index manager, e.g.await my_port.streams.remove(0)
. However, the methodremove
expects the index value of the instance.An easier way to delete an index is using method
<index_instance>.delete()
directly on the index instance, e.g.await my_stream.delete()
. The call of the function<index_instance>.delete()
will delete the index from the port, and will automatically notify the index manager about the deletion.
Session
A session
will be created automatically after a TCP connection is established between the client and the tester.
Three attributes of a session
are exposed:
is_online
- property to validate if the TCP connection is alive.
logoff()
- async method for gracefully closing the TCP connection to the tester.
sessions_info()
- async method for getting information of the current active sessions on a tester.
Session Identification
A tester does not use the tuple (source IP, source port, destination IP, destination port) to identify a session. Instead, it uses the username as the identification of a session. For instance,
tester = await testers.L23Tester("192.168.1.200", "JonDoe")
, where the username isJonDoe
.
Session Recovery and Resource Reallocation
To recover the session, the client only needs to establish a new TCP connection with the same username as the dropped session.
All resources of the broken session will be automatically transferred to the new session because they have the same username.
Handling Multiple Same-Username Sessions
If multiple sessions use the same username to connect to a tester after a broken session, the tester will give the control of the resources to a session in a first-come-first-served manner, and the others will be treated as observers. Thus, duplicated username should be avoided at the session level.
If the controlling session is disconnected, the tester will automatically pass the control of the resources to the next session in the queue.
Local State
The access to the local state of a resource is done through property <resource>.info
. The info contains current status of the resource and information of its attributes, which cannot be changed during a running session
.