.yml down
18 | ```
19 |
20 | On most of the systems, `docker-compose` needs to be installed separately even if the docker is already installed. For more information, see [docker prerequisites](prerequisites.md#docker) .
21 |
22 | >All the scenarios that are mentioned in the following sections, describe both manual and automated (requiring docker-compose) steps.
23 |
24 | ## Deployment Parameters
25 |
26 | ### Controller
27 |
28 | | Controller Parameters | Optional | Default | Description |
29 | |-----------------------------|-----------|-------------------------|-----------------------------------------------------------------|
30 | | --debug | Yes | false | Enables high volume logs with debug info for better diagnostics.|
31 | | --disable-app-usage-reporter| Yes | false | Disables sending of usage data to the app-usage-reporter. |
32 | | --http-port | Yes | 8443 | TCP port for HTTP server. |
33 | | --aur-host | Yes | https://localhost:5600 | Overrides the location of the app-usage-reporter. |
34 | | --accept-eula | No | NA | Indicates that the user has accepted EULA, otherwise the controller will not boot up. |
35 | | --license-servers | No | NA | Indicates the ip address of license servers for commercial users. |
36 |
37 | Docker Parameters:
38 |
39 | - `--net=host`: It is recommended to allow the use of the host network stack, in order to address the traffic-engine containers using `localhost` instead of `container-ip`, when deployed on the same host.
40 | - `-d`: This starts the container in background.
41 |
42 | Example:
43 |
44 | ```bash
45 | # For community users
46 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula --debug --http-port 5050
47 |
48 | # For commercial users
49 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula --debug --http-port 5050 --license-servers="ip/hostname of license server"
50 | ```
51 |
52 | ### Traffic Engine
53 |
54 | | Environment Variables | Optional | Default | Description |
55 | |-----------------------------|-----------|-------------------------|-----------------------------------------------------------------|
56 | | ARG_IFACE_LIST | No | NA | Name of the network interface to bind to. It must be visible to the traffic-engine's network namespace. For example, `virtual@af_packet,eth1` where `eth1` is the interface name and `virtual@af_packet` indicates that the interface is managed by the host kernel's network stack.|
57 | | OPT_LISTEN_PORT | Yes | "5555" | TCP port on which the controller can establish connection with the traffic-engine.|
58 | | OPT_NO_HUGEPAGES | Yes | "No" | If set to `Yes`, it disables hugepages in the OS. The hugepages needs to be disabled when the network interfaces are managed by the host kernel's stack.|
59 |
60 | Docker Parameters:
61 |
62 | - `--net=host`: This is required if the traffic-engine needs to bind to a network interface that is visible in the host network stack but not inside the docker's network.
63 | - `--privileged`: This is required because the traffic-engine needs to exercise capabilities that require elevated privileges.
64 | - `--cpuset-cpus`: The traffic-engine usually requires 1 shared CPU core for management activities and 2 exclusive CPU cores, each for the transmit engine and receive engine. The shared CPU core can be shared across multiple traffic-engines. For example, `--cpuset-cpus="0,1,2"` which indicates that cpu0 is shared, cpu1 is used for transmit and cpu2 is used for receive. If CPU cores are not specified, any arbitrary CPU cores will be chosen.
65 | > If enough CPU cores are not provided, the available CPU cores may be shared among management, transmit, and the receive engines, that can occasionally result in lower performance.
66 | - `-d`: This starts the container in background.
67 |
68 | Example:
69 |
70 | ```bash
71 | docker run --net=host --privileged -d \
72 | -e OPT_LISTEN_PORT="5555" \
73 | -e ARG_IFACE_LIST="virtual@af_packet,eth1" \
74 | -e OPT_NO_HUGEPAGES="Yes" \
75 | --cpuset-cpus="0,1,2" \
76 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine
77 | ```
78 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributor Covenant Code of Conduct
3 |
4 | ## Our Pledge
5 |
6 | We as members, contributors, and leaders pledge to make participation in our
7 | community a harassment-free experience for everyone, regardless of age, body
8 | size, visible or invisible disability, ethnicity, sex characteristics, gender
9 | identity and expression, level of experience, education, socio-economic status,
10 | nationality, personal appearance, race, caste, color, religion, or sexual
11 | identity and orientation.
12 |
13 | We pledge to act and interact in ways that contribute to an open, welcoming,
14 | diverse, inclusive, and healthy community.
15 |
16 | ## Our Standards
17 |
18 | Examples of behavior that contributes to a positive environment for our
19 | community include:
20 |
21 | * Demonstrating empathy and kindness toward other people
22 | * Being respectful of differing opinions, viewpoints, and experiences
23 | * Giving and gracefully accepting constructive feedback
24 | * Accepting responsibility and apologizing to those affected by our mistakes,
25 | and learning from the experience
26 | * Focusing on what is best not just for us as individuals, but for the overall
27 | community
28 |
29 | Examples of unacceptable behavior include:
30 |
31 | * The use of sexualized language or imagery, and sexual attention or advances of
32 | any kind
33 | * Trolling, insulting or derogatory comments, and personal or political attacks
34 | * Public or private harassment
35 | * Publishing others' private information, such as a physical or email address,
36 | without their explicit permission
37 | * Other conduct which could reasonably be considered inappropriate in a
38 | professional setting
39 |
40 | ## Enforcement Responsibilities
41 |
42 | Community leaders are responsible for clarifying and enforcing our standards of
43 | acceptable behavior and will take appropriate and fair corrective action in
44 | response to any behavior that they deem inappropriate, threatening, offensive,
45 | or harmful.
46 |
47 | Community leaders have the right and responsibility to remove, edit, or reject
48 | comments, commits, code, wiki edits, issues, and other contributions that are
49 | not aligned to this Code of Conduct, and will communicate reasons for moderation
50 | decisions when appropriate.
51 |
52 | ## Scope
53 |
54 | This Code of Conduct applies within all community spaces, and also applies when
55 | an individual is officially representing the community in public spaces.
56 | Examples of representing our community include using an official e-mail address,
57 | posting via an official social media account, or acting as an appointed
58 | representative at an online or offline event.
59 |
60 | ## Enforcement
61 |
62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
63 | reported to the community leaders responsible for enforcement at
64 | open-traffic-generator@googlegroups.com.
65 | All complaints will be reviewed and investigated promptly and fairly.
66 |
67 | All community leaders are obligated to respect the privacy and security of the
68 | reporter of any incident.
69 |
70 | ## Enforcement Guidelines
71 |
72 | Community leaders will follow these Community Impact Guidelines in determining
73 | the consequences for any action they deem in violation of this Code of Conduct:
74 |
75 | ### 1. Correction
76 |
77 | **Community Impact**: Use of inappropriate language or other behavior deemed
78 | unprofessional or unwelcome in the community.
79 |
80 | **Consequence**: A private, written warning from community leaders, providing
81 | clarity around the nature of the violation and an explanation of why the
82 | behavior was inappropriate. A public apology may be requested.
83 |
84 | ### 2. Warning
85 |
86 | **Community Impact**: A violation through a single incident or series of
87 | actions.
88 |
89 | **Consequence**: A warning with consequences for continued behavior. No
90 | interaction with the people involved, including unsolicited interaction with
91 | those enforcing the Code of Conduct, for a specified period of time. This
92 | includes avoiding interactions in community spaces as well as external channels
93 | like social media. Violating these terms may lead to a temporary or permanent
94 | ban.
95 |
96 | ### 3. Temporary Ban
97 |
98 | **Community Impact**: A serious violation of community standards, including
99 | sustained inappropriate behavior.
100 |
101 | **Consequence**: A temporary ban from any sort of interaction or public
102 | communication with the community for a specified period of time. No public or
103 | private interaction with the people involved, including unsolicited interaction
104 | with those enforcing the Code of Conduct, is allowed during this period.
105 | Violating these terms may lead to a permanent ban.
106 |
107 | ### 4. Permanent Ban
108 |
109 | **Community Impact**: Demonstrating a pattern of violation of community
110 | standards, including sustained inappropriate behavior, harassment of an
111 | individual, or aggression toward or disparagement of classes of individuals.
112 |
113 | **Consequence**: A permanent ban from any sort of public interaction within the
114 | community.
115 |
116 | ## Attribution
117 |
118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119 | version 2.1, available at
120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
121 |
122 | Community Impact Guidelines were inspired by
123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
127 | [https://www.contributor-covenant.org/translations][translations].
128 |
129 | [homepage]: https://www.contributor-covenant.org
130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
131 | [Mozilla CoC]: https://github.com/mozilla/diversity
132 | [FAQ]: https://www.contributor-covenant.org/faq
133 | [translations]: https://www.contributor-covenant.org/translations
134 |
135 |
--------------------------------------------------------------------------------
/deployments/k8s/network-emulation/ipfwd.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "time"
6 |
7 | "github.com/open-traffic-generator/snappi/gosnappi"
8 | )
9 |
10 | func main() {
11 |
12 | // This test configures one IPv4 interface on tx and rx test port each
13 | // and sends IPv4 packets with src IP address of interface on tx port
14 | // and dst IP address of interface on rx port. The packets are sent only
15 | // after ARP entries have been learned from corresponding DUT interfaces
16 | // and flow metrics is used to validate tx and rx counters.
17 |
18 | testConst := map[string]interface{}{
19 | "apiLocation": "https://localhost:8443",
20 | "txPortLocation": "eth1",
21 | "rxPortLocation": "eth2",
22 | "speed": "speed_1_gbps",
23 | "pktRate": uint64(50),
24 | "pktCount": uint32(100),
25 | "pktSize": uint32(128),
26 | "txMac": "00:00:01:01:01:01",
27 | "txIp": "1.1.1.1",
28 | "txGateway": "1.1.1.2",
29 | "txPrefix": uint32(24),
30 | "rxMac": "00:00:01:01:01:02",
31 | "rxIp": "2.2.2.1",
32 | "rxGateway": "2.2.2.2",
33 | "rxPrefix": uint32(24),
34 | }
35 |
36 | api := gosnappi.NewApi()
37 | api.NewHttpTransport().SetLocation(testConst["apiLocation"].(string)).SetVerify(false)
38 |
39 | log.Println("Constructing OTG configuration ...")
40 | c := otgConfig(testConst)
41 |
42 | log.Println("Pushing OTG configuration ...")
43 | if wrn, err := api.SetConfig(c); err != nil {
44 | log.Fatal(err)
45 | } else {
46 | for _, w := range wrn.Warnings() {
47 | log.Println("WARNING:", w)
48 | }
49 | }
50 |
51 | log.Println("Waiting for ARP entries for configured interfaces ...")
52 | for start := time.Now(); !ipNeighborsOk(api, testConst); time.Sleep(time.Millisecond * 500) {
53 | if time.Second*30 < time.Until(start) {
54 | log.Fatal("Timed out waiting for ARP entries")
55 | }
56 | }
57 |
58 | log.Println("Starting flow transmission ...")
59 | cs := gosnappi.NewControlState()
60 | cs.Traffic().FlowTransmit().SetState(gosnappi.StateTrafficFlowTransmitState.START)
61 | if wrn, err := api.SetControlState(cs); err != nil {
62 | log.Fatal(err)
63 | } else {
64 | for _, w := range wrn.Warnings() {
65 | log.Println("WARNING:", w)
66 | }
67 | }
68 |
69 | log.Println("Waiting for flow metrics ...")
70 | for start := time.Now(); !flowMetricsOk(api, testConst); time.Sleep(time.Millisecond * 500) {
71 | if time.Second*30 < time.Until(start) {
72 | log.Fatal("Timed out waiting for ARP entries")
73 | }
74 | }
75 | }
76 |
77 | func otgConfig(tc map[string]interface{}) gosnappi.Config {
78 | c := gosnappi.NewConfig()
79 |
80 | ptx := c.Ports().Add().SetName("ptx").SetLocation(tc["txPortLocation"].(string))
81 | prx := c.Ports().Add().SetName("prx").SetLocation(tc["rxPortLocation"].(string))
82 |
83 | c.Layer1().Add().
84 | SetName("ly").
85 | SetPortNames([]string{ptx.Name(), prx.Name()}).
86 | SetSpeed(gosnappi.Layer1SpeedEnum(tc["speed"].(string)))
87 |
88 | dtx := c.Devices().Add().SetName("dtx")
89 | drx := c.Devices().Add().SetName("drx")
90 |
91 | dtxEth := dtx.Ethernets().
92 | Add().
93 | SetName("dtxEth").
94 | SetMac(tc["txMac"].(string)).
95 | SetMtu(1500)
96 |
97 | dtxEth.Connection().SetPortName(ptx.Name())
98 |
99 | dtxIp := dtxEth.
100 | Ipv4Addresses().
101 | Add().
102 | SetName("dtxIp").
103 | SetAddress(tc["txIp"].(string)).
104 | SetGateway(tc["txGateway"].(string)).
105 | SetPrefix(tc["txPrefix"].(uint32))
106 |
107 | drxEth := drx.Ethernets().
108 | Add().
109 | SetName("drxEth").
110 | SetMac(tc["rxMac"].(string)).
111 | SetMtu(1500)
112 |
113 | drxEth.Connection().SetPortName(prx.Name())
114 |
115 | drxIp := drxEth.
116 | Ipv4Addresses().
117 | Add().
118 | SetName("drxIp").
119 | SetAddress(tc["rxIp"].(string)).
120 | SetGateway(tc["rxGateway"].(string)).
121 | SetPrefix(tc["rxPrefix"].(uint32))
122 |
123 | flow := c.Flows().Add()
124 | flow.SetName("ftxV4")
125 | flow.Duration().FixedPackets().SetPackets(tc["pktCount"].(uint32))
126 | flow.Rate().SetPps(tc["pktRate"].(uint64))
127 | flow.Size().SetFixed(tc["pktSize"].(uint32))
128 | flow.Metrics().SetEnable(true)
129 |
130 | flow.TxRx().Device().
131 | SetTxNames([]string{dtxIp.Name()}).
132 | SetRxNames([]string{drxIp.Name()})
133 |
134 | ftxV4Eth := flow.Packet().Add().Ethernet()
135 | ftxV4Eth.Src().SetValue(dtxEth.Mac())
136 |
137 | ftxV4Ip := flow.Packet().Add().Ipv4()
138 | ftxV4Ip.Src().SetValue(tc["txIp"].(string))
139 | ftxV4Ip.Dst().SetValue(tc["rxIp"].(string))
140 |
141 | log.Printf("Config:\n%v\n", c)
142 | return c
143 | }
144 |
145 | func ipNeighborsOk(api gosnappi.Api, tc map[string]interface{}) bool {
146 | count := 0
147 |
148 | log.Println("Getting IPv4 neighbors ...")
149 | req := gosnappi.NewStatesRequest()
150 | // query to fetch states for all configured IPv4 interfaces
151 | req.Ipv4Neighbors()
152 | states, err := api.GetStates(req)
153 | if err != nil {
154 | log.Fatal(err)
155 | }
156 |
157 | log.Printf("IPv4 Neighbors Metrics:\n%v\n", states)
158 | for _, n := range states.Ipv4Neighbors().Items() {
159 | if n.HasLinkLayerAddress() {
160 | for _, key := range []string{"txGateway", "rxGateway"} {
161 | if n.Ipv4Address() == tc[key].(string) {
162 | count += 1
163 | }
164 | }
165 | }
166 | }
167 |
168 | return count == 2
169 | }
170 |
171 | func flowMetricsOk(api gosnappi.Api, tc map[string]interface{}) bool {
172 | pktCount := uint64(tc["pktCount"].(uint32))
173 |
174 | log.Println("Getting flow metrics ...")
175 | req := gosnappi.NewMetricsRequest()
176 | // query to fetch metrics for all configured flows
177 | req.Flow()
178 | metrics, err := api.GetMetrics(req)
179 | if err != nil {
180 | log.Fatal(err)
181 | }
182 |
183 | log.Printf("Flow Metrics:\n%v\n", metrics)
184 | for _, m := range metrics.FlowMetrics().Items() {
185 | if m.Transmit() != gosnappi.FlowMetricTransmit.STOPPED ||
186 | m.FramesTx() != pktCount ||
187 | m.FramesRx() != pktCount {
188 | return false
189 | }
190 |
191 | }
192 |
193 | return true
194 | }
195 |
--------------------------------------------------------------------------------
/docs/res/hw-server.drawio:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/docs/stylesheets/extra.css:
--------------------------------------------------------------------------------
1 | [data-md-color-scheme="ks-light"] {
2 | --ks-color-black: #000000;
3 | --ks-color-dark-red: #871518;
4 | --ks-color-red: #E90029;
5 | --ks-color-dark-gray: #373A36;
6 | --ks-color-medium-gray: #97999B;
7 | --ks-color-gray: #D9D9D6;
8 | --ks-color-light-gray: #EBEBEB;
9 | --ks-color-white: #FFFFFF;
10 |
11 | --ks-color-dark-blue: #071D49;
12 | --ks-color-blue: #426DA9;
13 | --ks-color-teal: #63B1BC;
14 |
15 | --gh-color-code-bg-color: rgb(246,248,250);
16 | --gh-color-code-fg-color: rgb(36,41,47);
17 | --gh-color-code-comment-color: rgb(110,119,129);
18 | --gh-color-code-string-color: rgb(10,48,105);
19 | --gh-color-code-keyword-color: rgb(207,34,46);
20 | --gh-color-code-pretty-color: rgb(130,80,223);
21 |
22 | --md-primary-fg-color: var(--ks-color-black);
23 | --md-primary-fg-color--light: var(--ks-color-light-gray);
24 | --md-primary-fg-color--dark: var(--ks-color-dark-gray);
25 | --md-primary-bg-color: var(--ks-color-white);
26 |
27 | --md-default-bg-color: var(--ks-color-white);
28 | --md-default-fg-color--light: var(--ks-color-dark-gray);
29 | --md-default-fg-color: var(--ks-color-black);
30 | --md-default-fg-color--dark: var(--ks-color-black);
31 |
32 | --md-typeset-color: var(--ks-color-dark-gray);
33 | --md-typeset-a-color: var(--ks-color-red);
34 |
35 | --md-code-bg-color: var(--gh-color-code-bg-color);
36 | --md-code-fg-color: var(--gh-color-code-fg-color);
37 | --md-code-hl-comment-color: var(--gh-color-code-comment-color);
38 | --md-code-hl-variable-color: var(--gh-color-code-pretty-color);
39 | --md-code-hl-name-color: var(--gh-color-code-pretty-color);
40 | --md-code-hl-number-color: var(--gh-color-code-string-color);
41 | --md-code-hl-string-color: var(--gh-color-code-string-color);
42 | --md-code-hl-special-color: var(--gh-color-code-fg-color);
43 | --md-code-hl-operator-color: var(--gh-color-code-string-color);
44 | --md-code-hl-punctuation-color: var(--gh-color-code-fg-color);
45 | --md-code-hl-keyword-color: var(--gh-color-code-keyword-color);
46 | --md-code-hl-function-color: var(--gh-color-code-pretty-color);
47 | --md-code-hl-constant-color: var(--gh-color-code-pretty-color);
48 |
49 | --md-admonition-bg-color: var(--gh-color-code-bg-color);
50 | --md-admonition-fg-color: var(--md-default-fg-color--light);
51 | }
52 | [data-md-color-scheme="ks-dark"] {
53 | --ks-color-black: #000000;
54 | --ks-color-dark-red: #871518;
55 | --ks-color-red: #E90029;
56 | --ks-color-dark-gray: #373A36;
57 | --ks-color-medium-gray: #97999B;
58 | --ks-color-gray: #D9D9D6;
59 | --ks-color-light-gray: #EBEBEB;
60 | --ks-color-white: #FFFFFF;
61 |
62 | --ks-color-dark-blue: #071D49;
63 | --ks-color-blue: #426DA9;
64 | --ks-color-teal: #63B1BC;
65 |
66 | --gh-color-code-bg-color: rgb(22,27,24);
67 | --gh-color-code-fg-color: rgb(201,209,207);
68 | --gh-color-code-comment-color: rgb(139,148,158);
69 | --gh-color-code-string-color: rgb(165,214,255);
70 | --gh-color-code-keyword-color: rgb(255,123,114);
71 | --gh-color-code-pretty-color: rgb(210,168,255);
72 |
73 | --md-primary-fg-color: var(--ks-color-black);
74 | --md-primary-fg-color--dark: var(--ks-color-dark-gray);
75 | --md-primary-fg-color--light: var(--ks-color-medium-gray);
76 | --md-primary-bg-color: var(--ks-color-white);
77 | --md-primary-bg-color--light: var(--ks-color-light-gray);
78 |
79 | --md-default-bg-color--light: var(--ks-color-white);
80 | --md-default-bg-color--lighter: var(--ks-color-white);
81 | --md-default-bg-color--lightest: var(--ks-color-white);
82 | --md-default-fg-color: var(--ks-color-white);
83 | --md-default-fg-color--lightest: var(--ks-color-blue);
84 | --md-default-fg-color--light: var(--ks-color-light-gray);
85 | --md-default-fg-color--lighter: var(--ks-color-light-gray);
86 | --md-default-fg-color--dark: var(--ks-color-medium-gray);
87 | --md-default-bg-color: var(--ks-color-black);
88 |
89 | --md-typeset-color: var(--ks-color-gray);
90 | --md-typeset-a-color: var(--ks-color-red);
91 |
92 | --md-code-bg-color: var(--gh-color-code-bg-color);
93 | --md-code-fg-color: var(--gh-color-code-fg-color);
94 | --md-code-hl-comment-color: var(--gh-color-code-comment-color);
95 | --md-code-hl-variable-color: var(--gh-color-code-pretty-color);
96 | --md-code-hl-name-color: var(--gh-color-code-pretty-color);
97 | --md-code-hl-number-color: var(--gh-color-code-string-color);
98 | --md-code-hl-string-color: var(--gh-color-code-string-color);
99 | --md-code-hl-special-color: var(--gh-color-code-fg-color);
100 | --md-code-hl-operator-color: var(--gh-color-code-string-color);
101 | --md-code-hl-punctuation-color: var(--gh-color-code-fg-color);
102 | --md-code-hl-keyword-color: var(--gh-color-code-keyword-color);
103 | --md-code-hl-function-color: var(--gh-color-code-pretty-color);
104 | --md-code-hl-constant-color: var(--gh-color-code-pretty-color);
105 |
106 | --md-admonition-bg-color: var(--md-default-bg-color);
107 | --md-admonition-fg-color: var(--md-default-fg-color--light);
108 |
109 | --md-accent-fg-color: var(--ks-color-light-gray);
110 | --md-accent-fg-color--transparent: var(--ks-color-dark-gray);
111 | }
112 |
113 | .md-grid {
114 | max-width: 1440px;
115 | }
116 |
117 | .container {
118 | display: flex;
119 | }
120 |
121 | .column {
122 | flex: 1;
123 | padding: 20px;
124 | }
125 |
--------------------------------------------------------------------------------
/docs/developer/snappi-install.md:
--------------------------------------------------------------------------------
1 | # Installing Snappi
2 |
3 | The procedures explained in this section helps to install and configure snappi for an Open Traffic Generator API.
4 |
5 | The test scripts written in **gosnappi**, and the auto-generated Go SDK, can be executed against any traffic generator that conforms to [Open Traffic Generator API](https://github.com/open-traffic-generator/models).
6 |
7 | [Ixia-c](https://github.com/open-traffic-generator/ixia-c) is one of such reference implementations of the Open Traffic Generator API.
8 |
9 | ## To install Snappi for the Go language, do the following:
10 |
11 | ### Setup the client
12 |
13 | ```sh
14 | go get github.com/open-traffic-generator/snappi/gosnappi
15 | ```
16 |
17 | ### Start Testing
18 |
19 | ```Go
20 | package examples
21 |
22 | import (
23 | "encoding/hex"
24 | "testing"
25 | "time"
26 |
27 | "github.com/open-traffic-generator/snappi/gosnappi"
28 | )
29 |
30 | func TestQuickstart(t *testing.T) {
31 | // Create a new API handle to make API calls against OTG
32 | api := gosnappi.NewApi()
33 |
34 | // Set the transport protocol to HTTP
35 | api.NewHttpTransport().SetLocation("https://localhost:8443")
36 |
37 | // Create a new traffic configuration that will be set on OTG
38 | config := gosnappi.NewConfig()
39 |
40 | // Add a test port to the configuration
41 | ptx := config.Ports().Add().SetName("ptx").SetLocation("veth-a")
42 |
43 | // Configure a flow and set previously created test port as one of endpoints
44 | flow := config.Flows().Add().SetName("f1")
45 | flow.TxRx().Port().SetTxName(ptx.Name())
46 | // and enable tracking flow metrics
47 | flow.Metrics().SetEnable(true)
48 |
49 | // Configure number of packets to transmit for previously configured flow
50 | flow.Duration().FixedPackets().SetPackets(100)
51 | // and fixed byte size of all packets in the flow
52 | flow.Size().SetFixed(128)
53 |
54 | // Configure protocol headers for all packets in the flow
55 | pkt := flow.Packet()
56 | eth := pkt.Add().Ethernet()
57 | ipv4 := pkt.Add().Ipv4()
58 | udp := pkt.Add().Udp()
59 | cus := pkt.Add().Custom()
60 |
61 | eth.Dst().SetValue("00:11:22:33:44:55")
62 | eth.Src().SetValue("00:11:22:33:44:66")
63 |
64 | ipv4.Src().SetValue("10.1.1.1")
65 | ipv4.Dst().SetValue("20.1.1.1")
66 |
67 | // Configure repeating patterns for source and destination UDP ports
68 | udp.SrcPort().SetValues([]int32{5010, 5015, 5020, 5025, 5030})
69 | udp.DstPort().Increment().SetStart(6010).SetStep(5).SetCount(5)
70 |
71 | // Configure custom bytes (hex string) in payload
72 | cus.SetBytes(hex.EncodeToString([]byte("..QUICKSTART SNAPPI..")))
73 |
74 | // Optionally, print JSON representation of config
75 | if j, err := config.ToJson(); err != nil {
76 | t.Fatal(err)
77 | } else {
78 | t.Log("Configuration: ", j)
79 | }
80 |
81 | // Push traffic configuration constructed so far to OTG
82 | if _, err := api.SetConfig(config); err != nil {
83 | t.Fatal(err)
84 | }
85 |
86 | // Start transmitting the packets from configured flow
87 | ts := gosnappi.NewTransmitState()
88 | ts.SetState(gosnappi.TransmitStateState.START)
89 | if _, err := api.SetTransmitState(ts); err != nil {
90 | t.Fatal(err)
91 | }
92 |
93 | // Fetch metrics for configured flow
94 | req := gosnappi.NewMetricsRequest()
95 | req.Flow().SetFlowNames([]string{flow.Name()})
96 | // and keep polling until either expectation is met or deadline exceeds
97 | deadline := time.Now().Add(10 * time.Second)
98 | for {
99 | metrics, err := api.GetMetrics(req)
100 | if err != nil || time.Now().After(deadline) {
101 | t.Fatalf("err = %v || deadline exceeded", err)
102 | }
103 | // print YAML representation of flow metrics
104 | t.Log(metrics)
105 | if metrics.FlowMetrics().Items()[0].Transmit() == gosnappi.FlowMetricTransmit.STOPPED {
106 | break
107 | }
108 | time.Sleep(100 * time.Millisecond)
109 | }
110 | }
111 | ```
112 |
113 | ## To install Snappi for the Python language, do the following:
114 |
115 | ### Setup the Client
116 |
117 | ```sh
118 | python -m pip install --upgrade snappi
119 | ```
120 |
121 | ### Start Testing
122 |
123 | ```python
124 | import datetime
125 | import time
126 | import snappi
127 | import pytest
128 |
129 |
130 | @pytest.mark.example
131 | def test_quickstart():
132 | # Create a new API handle to make API calls against OTG
133 | # with HTTP as default transport protocol
134 | api = snappi.api(location="https://localhost:8443")
135 |
136 | # Create a new traffic configuration that will be set on OTG
137 | config = api.config()
138 |
139 | # Add a test port to the configuration
140 | ptx = config.ports.add(name="ptx", location="veth-a")
141 |
142 | # Configure a flow and set previously created test port as one of endpoints
143 | flow = config.flows.add(name="flow")
144 | flow.tx_rx.port.tx_name = ptx.name
145 | # and enable tracking flow metrics
146 | flow.metrics.enable = True
147 |
148 | # Configure number of packets to transmit for previously configured flow
149 | flow.duration.fixed_packets.packets = 100
150 | # and fixed byte size of all packets in the flow
151 | flow.size.fixed = 128
152 |
153 | # Configure protocol headers for all packets in the flow
154 | eth, ip, udp, cus = flow.packet.ethernet().ipv4().udp().custom()
155 |
156 | eth.src.value = "00:11:22:33:44:55"
157 | eth.dst.value = "00:11:22:33:44:66"
158 |
159 | ip.src.value = "10.1.1.1"
160 | ip.dst.value = "20.1.1.1"
161 |
162 | # Configure repeating patterns for source and destination UDP ports
163 | udp.src_port.values = [5010, 5015, 5020, 5025, 5030]
164 | udp.dst_port.increment.start = 6010
165 | udp.dst_port.increment.step = 5
166 | udp.dst_port.increment.count = 5
167 |
168 | # Configure custom bytes (hex string) in payload
169 | cus.bytes = "".join([hex(c)[2:] for c in b"..QUICKSTART SNAPPI.."])
170 |
171 | # Optionally, print JSON representation of config
172 | print("Configuration: ", config.serialize(encoding=config.JSON))
173 |
174 | # Push traffic configuration constructed so far to OTG
175 | api.set_config(config)
176 |
177 | # Start transmitting the packets from configured flow
178 | ts = api.transmit_state()
179 | ts.state = ts.START
180 | api.set_transmit_state(ts)
181 |
182 | # Fetch metrics for configured flow
183 | req = api.metrics_request()
184 | req.flow.flow_names = [flow.name]
185 | # and keep polling until either expectation is met or deadline exceeds
186 | start = datetime.datetime.now()
187 | while True:
188 | metrics = api.get_metrics(req)
189 | if (datetime.datetime.now() - start).seconds > 10:
190 | raise Exception("deadline exceeded")
191 | # print YAML representation of flow metrics
192 | print(metrics)
193 | if metrics.flow_metrics[0].transmit == metrics.flow_metrics[0].STOPPED:
194 | break
195 | time.sleep(0.1)
196 | ```
197 |
--------------------------------------------------------------------------------
/docs/licensing.md:
--------------------------------------------------------------------------------
1 | # Licensing
2 |
3 | ## License Editions
4 |
5 | The following License Editions are available for Keysight Elastic Network Generator:
6 |
7 | | Capability | Community | Developer | Team | System |
8 | |-------------------------------------|----------------------|----------------------|--------------------------------|-------------------------------------|
9 | | Ixia-c Traffic Port Capacity | 4 x 1/10GE | 50GE | 400GE | 800GE |
10 | | Test Concurrency | 1 Seat | 1 Seat | 8 Seats | 16 Seats |
11 | | Protocol Scale | Restricted | Limited | Limited | Unlimited |
12 | | Requires a valid license | N | Y | Y | Y |
13 | | Includes Ixia-c Software Test Ports | Y | Y | Y | Y |
14 | | Works with UHD400T Hardware | N | N | Y | Y |
15 | | Works with IxOS Hardware | N | N | N | Y |
16 |
17 | The **Ixia-c Traffic Port Capacity** is determined as a sum of the configured Ixia-c software test port speeds with the possible values of: 100GE, 50GE, 40GE, 25GE, 10GE, and 1GE. The Maximum data plane performance of an Ixia-c port may be less than the configured port speed, depending on the capabilities of the underlying hardware and software drivers. Doesn't apply to the UHD400T and IxOS hardware.
18 |
19 | The **Test seat concurrency** applies to a number of controller instances that are running with a configuration that exceeds the capabilities of the Community Edition.
20 |
21 | The **Restricted** protocol scale supports the maximum of 4 BGP sessions per test.
22 |
23 | The Capabilities of the **Limited** protocol scale depend on the protocol. For details, contact [Keysight Support](https://support.ixiacom.com/contact/support).
24 |
25 | Keysight Elastic Network Generator can simultaneously consume multiple licenses to increase the capabilities of a test. For example, if the Ixia-c Traffic Port Capacity configured in one test is 100GE, two Developer licenses will be consumed if available.
26 |
27 | If you require capabilities beyond those provided by the Community Edition, use [Keysight Elastic Network Generator](https://www.keysight.com/us/en/products/network-test/protocol-load-test/keysight-elastic-network-generator.html) product page to request an evaluation or a quote.
28 |
29 | ## License Server
30 |
31 | In order to use capabilities of Elastic Network Generator that require a valid license, you need to deploy a Keysight License Server. Keysight uses the license server to manage floating or network shared licenses for its software products. The license server enables licenses to float and not be tied to a specific Elastic Network Generator instance. The Elastic Network Generator controllers must be able to reach the License server.
32 |
33 | ### Deployment
34 |
35 | The license server is a virtual machine and it is distributed as OVA and QCOW2 images (you only need one of them depending on your hypervisor, downloading may need credentials for support website.).
36 |
37 | * [OVA image](https://downloads.ixiacom.com/support/downloads_and_updates/public/KENG-License-Server-VM/4.4.0/4.4.0-202/slum-4.4.0-202.ova), 5.8GB
38 | * [QCOW2 image](https://downloads.ixiacom.com/support/downloads_and_updates/public/KENG-License-Server-VM/4.4.0/4.4.0-202/slum-4.4.0-202.qcow2), 6GB
39 |
40 | To make a decision where to deploy the License Server VM, take into the account the following requirements:
41 |
42 | * For VMware ESXi, use the OVA image
43 | * For Linux-based QEMU or KVM, use the QCOW2 image
44 | * 2 vCPU cores
45 | * 8GB RAM for ESXi, 4GB RAM for QEMU/KVM
46 | * 100GB storage
47 | * 1 vNIC for network connectivity. Note that DHCP is the preferred option, and this is also how the VM is configured to obtain its IP address.
48 |
49 | Network connectivity requirements for the License Server VM
50 |
51 | 1. Internet access from the VM over HTTPS is desirable for online license activation, but not strictly required. Offline activation method is available as well.
52 | 2. Access from a user over HTTPS (TCP/443) for license operations (activation, deactivation, reservation, sync).
53 | 3. Access from any `keng-controller` that needs a license during a test run over gRPC (TCP/7443) for license checkout and check-in.
54 |
55 | Here is an example of how different components communicate with the License Server:
56 |
57 | 
58 |
59 | ### Configuration
60 |
61 | If your network doesn't provide DHCP, you can configure a static IP address for the License Server VM. Access the VM console and go through two-step login process:
62 |
63 | * First prompt: `console` (no password)
64 | * Second promt: `admin`/`admin`
65 | * Run the following commands to configure a static IP address, where `x.x.x.x` is the IP address, `yy` is the prefix length, `z.z.z.z` is the default gateway, `a.a.a.a` and `b.b.b.b` are DNS servers:
66 |
67 | ```Shell
68 | kcos networking ip set mgmt0 x.x.x.x/yy z.z.z.z
69 | kcos networking dns-servers add a.a.a.a b.b.b.b
70 | ```
71 |
72 | ### Activation
73 |
74 | Now you shall be able to activate licenses and use the License Server with your Elastic Network Generator environments. Go to `https://your-license-server-hostname` to access the application. Enter credentials: `admin`/`admin` to login.
75 |
76 | If you have an activation code, to perform an online activation, click "Activate Licenses", enter the code and click "Activate". For offline mode, choose "Offline Operations" instead.
77 |
78 | You can also use a command-line session, via console or SSH, to perform license operations. Run `kcos licensing --help` to see the list of available commands.
79 |
80 | ## Using Licenses
81 |
82 | To use the licenses with the Elastic Network Generator, provide the location of the license servers to the controller instances using
83 |
84 | ```
85 | --license-servers="server1 server2 server3 server4"
86 | ```
87 |
88 | argument when launching the controller. The argument accepts a space-separated list of hostnames or IP addresses of the License Servers, up to four. The controller will try to connect to the License Servers in the order they are specified in the list. If the first License Server is not available, or doesn't have enough available licenses to run the test, the controller will try to connect to the next one in the list.
89 |
90 | An alternative way is to use an environment variable `LICENSE_SERVERS`.
91 |
92 | ## Additional Information
93 |
94 | Please refer to the [Reference Guide](reference/licensing.md) for more information on the license operations.
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ixia-C
4 |
5 |
6 |
7 |
8 | Ixia-C - A powerful traffic generator based on Open Traffic Generator API
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Quick Start •
22 | Key Features •
23 | Documentation •
24 | Use Cases •
25 | Roadmap •
26 | FAQ
27 |
28 |
29 |
30 | ### What is Ixia-C ?
31 |
32 | - A modern, powerful and **API-driven** traffic generator designed to cater to the needs of hyper-scalers, network hardware vendors and hobbyists alike.
33 |
34 | - **Free for basic use-cases** and distributed / deployed as a multi-container application consisting primarily of a [controller](https://github.com/orgs/open-traffic-generator/packages/container/package/keng-controller), a [traffic-engine](https://github.com/orgs/open-traffic-generator/packages/container/package/ixia-c-traffic-engine) and a [protocol-engine](https://github.com/orgs/open-traffic-generator/packages/container/package/ixia-c-protocol-engine).
35 |
36 | - As a reference implementation of [Open Traffic Generator API](https://github.com/open-traffic-generator/models), supports client SDKs in various languages, most prevalent being [snappi](https://github.com/open-traffic-generator/snappi) (Python SDK) and [gosnappi](https://github.com/open-traffic-generator/snappi/tree/main/gosnappi).
37 |
38 |
39 |
40 |
41 |
42 | > [Keysight](https://www.keysight.com) also offers a well supported commercial version, [Keysight Elastic Network Generator (KENG)](https://www.keysight.com/us/en/products/network-test/protocol-load-test/keysight-elastic-network-generator.html), with no restrictions on **performance and scalability**. Scripts written for the community version are **compatible** with this version.
43 |
44 | ### Quick Start
45 |
46 | Please ensure that following prerequisites are met by the setup:
47 | - At least **2 x86_64 CPU cores** and **7GB RAM**, preferably running **Ubuntu 22.04 LTS** OS
48 | - **Python 3.8+** (and **pip**) or **Go 1.19+**
49 | - **Docker Engine** (Community Edition)
50 |
51 |
52 | #### 1. Deploy Ixia-C
53 |
54 | ```bash
55 | # clone this repository
56 | git clone --recurse-submodules https://github.com/open-traffic-generator/ixia-c.git && cd ixia-c
57 |
58 | # create a veth pair and deploy ixia-c containers where one traffic-engine is bound
59 | # to each interface in the pair, and controller is configured to figure out how to
60 | # talk to those traffic-engine containers
61 | cd conformance && ./do.sh topo new dp
62 | ```
63 |
64 | #### 2. Setup and run standalone test using [snappi](https://github.com/open-traffic-generator/snappi) or [gosnappi](https://github.com/open-traffic-generator/snappi/tree/main/gosnappi)
65 |
66 | ```bash
67 | # change dir to conformance if you haven't already
68 | cd conformance
69 |
70 | # setup python virtual environment and install dependencies
71 | ./do.sh prepytest
72 |
73 | # run standalone snappi test that configures and sends UDP traffic
74 | # upon successful run, flow metrics shall be printed on console
75 | ./do.sh pytest examples/test_quickstart.py
76 |
77 | # optionally, go equivalent of the test can be run like so
78 | ./do.sh gotest examples/quickstart_test.go
79 | ```
80 |
81 | > Checkout the contents of [test_quickstart.py](https://github.com/open-traffic-generator/conformance/blob/22563e20fe512ef13baf44c1bc69bc59f87f6c25/examples/test_quickstart.py) and equivalent [quickstart_test.go](https://github.com/open-traffic-generator/conformance/blob/22563e20fe512ef13baf44c1bc69bc59f87f6c25/examples/quickstart_test.go) for quick explanation on test steps.
82 |
83 | #### 3. Optionally, run test using [curl](https://curl.se/)
84 |
85 | We can also pass equivalent **JSON configuration** directly to **controller**, just by using **curl**.
86 | The description of each node in the configuration is detailed in self-updating [online documentation](https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/open-traffic-generator/models/v1.42.0/artifacts/openapi.yaml).
87 |
88 |
89 | ```bash
90 | # push traffic configuration
91 | curl -skL https://localhost:8443/config -H "Content-Type: application/json" -d @conformance/examples/quickstart_config.json
92 |
93 | # start transmitting configured flows
94 | curl -skL https://localhost:8443/control/state -H "Content-Type: application/json" -d @conformance/examples/quickstart_control.json
95 |
96 | # fetch flow metrics
97 | curl -skL https://localhost:8443/monitor/metrics -H "Content-Type: application/json" -d @conformance/examples/quickstart_metrics.json
98 | ```
99 |
100 | ### Key Features
101 |
102 | * High Performance
103 | * Run on servers or Keysight hardware (commercial version only)
104 | * Generate kbps to Tbps of traffic using same script
105 | * 10Gbps @ 64 byte frame size using one Xeon class core (commercial version only)
106 | * Built using [DPDK](https://www.dpdk.org)
107 | * Fast REST API for automation
108 | * Easily integrate with test frameworks like [pytest](https://www.pytest.org)
109 | * Easily integrate into CI/CD pipelines with Jenkins, GitHub, GitLab
110 | * Up to 256 flows per port. Each Flow supports:
111 | * Packet Templates for Ethernet, VLAN, VXLAN, GTPv1, GTPv2, IPv4, IPv6, ICMP, ICMPv6, GRE, UDP, & TCP. More protocols are on the way.
112 | * Ability to use tools like Scapy to add headers for unsupported protocols.
113 | * Manipulation of any field in the packet headers
114 | * Patterns to modify common packet header fields to generate millions of unique packets
115 | * Ability to track flows based on common packet header fields
116 | * Configurable frame size
117 | * Rate specification in pps (packets per second) or % line-rate
118 | * Ability to send bursts
119 | * Statistics
120 | * Per-port and per-flow
121 | * One way latency measurements (min, max, average) on a per flow basis
122 | * Capture
123 | * Packets with filters
124 | * Write to PCAP or redirect to tcpdump
125 |
126 | ## Copyright notice
127 |
128 | © Copyright Keysight Technologies, Inc. 2021, 2022, 2023
129 |
130 |
--------------------------------------------------------------------------------
/deployments/k8s/network-emulation/readme.md:
--------------------------------------------------------------------------------
1 | # Ixia-C Kubernetes Deployment For Emulating Network Topology
2 |
3 | Ixia-C is a pure software-based solution for facilitating protocol emulation and traffic generation against containerized DUTs (Device Under Test).
4 |
5 |
6 |
7 |
8 |
9 | For deployment, K8S (Kubernetes) as a platform can be exercised using one of following approaches:
10 | - Manually construct K8S deployment files for any given topology
11 | - Use [KNE](https://github.com/openconfig/kne) to automate deployment using an abstract topology definition
12 |
13 | This document discusses the first approach using a minimal Ubuntu container as DUT.
14 |
15 | ### Overview
16 |
17 | Ixia-C is distributed as a multi-container application with following as core components:
18 | 1. `keng-controller`: API Server driving operation on one or more test port based on API requests
19 | 2. `ixia-c-traffic-engine`: Drives data plane operations on test port
20 | 3. `ixia-c-protocol-engine`: Drives control plane operations on test port
21 |
22 | Upon deployment, `ixia-c-traffic-engine` and `ixia-c-protocol-engine` need to share same network namespace and hence constitute an Ixia-C Test Port Pod. A typical deployment consists of one `controller pod` and one or more `test port pod`s.
23 |
24 | To establish any meaningful interaction between Ixia-C test ports and DUT interfaces, following needs to be done:
25 | - A pair of virtual interfaces need to be created, each on an Ixia-C test port pod and DUT pod
26 | - The interface pair then needs to have a P2P (point-to-point) link established among themselves
27 |
28 | To achieve this, we'll be using [Meshnet CNI](https://github.com/networkop/meshnet-cni).
29 |
30 |
31 | ### Prerequisites
32 |
33 | - At least **2 x86 CPU cores**, **7GB RAM** and **30GB Free Hard Disk Space**
34 | - Recommended OS is **Ubuntu 22.04 LTS** release.
35 | - Go **1.20+**
36 | - **Docker Engine** (Community Edition) - Needed when using kind for setting up K8S cluster
37 |
38 | ### Steps
39 |
40 | 1. Clone this repository
41 |
42 | ```bash
43 | git clone --recurse-submodules https://github.com/open-traffic-generator/ixia-c.git && cd ixia-c
44 | ```
45 |
46 | 2. (Optional) Setup a Kubernetes Cluster using [kind](https://kind.sigs.k8s.io/)
47 |
48 | ```bash
49 | # install kind
50 | go install sigs.k8s.io/kind@v0.20.0
51 |
52 | # create a single-node cluster
53 | kind create cluster --config=deployments/k8s/network-emulation/kind.yaml --wait 30s
54 |
55 | # get compatible kubectl
56 | sudo docker cp kind-control-plane:/usr/bin/kubectl /usr/local/bin/kubectl
57 | sudo chmod 0755 /usr/local/bin/kubectl
58 |
59 | # ensure all pods are ready
60 | kubectl wait --for=condition=Ready pods --all --all-namespaces
61 | ```
62 |
63 | 3. (Optional) If cluster does not have access to ghcr.io, manually download container images and push them to all nodes in the cluster
64 |
65 | ```bash
66 | # Enter Github user ID and PAT when asked for credentials
67 | docker login ghcr.io
68 |
69 | # download ixia-c images
70 | docker pull ghcr.io/open-traffic-generator/keng-controller:1.42.0-4
71 | docker pull ghcr.io/open-traffic-generator/ixia-c-traffic-engine:1.8.0.245
72 | docker pull ghcr.io/open-traffic-generator/ixia-c-protocol-engine:1.00.0.488
73 |
74 | # download DUT image
75 | docker pull ubuntu:22.04
76 |
77 | # download meshnet image
78 | docker pull networkop/meshnet:latest
79 | # download init image
80 | docker pull networkop/init-wait:latest
81 |
82 | # push images to nodes
83 | kind load docker-image ghcr.io/open-traffic-generator/keng-controller:1.42.0-4
84 | kind load docker-image ghcr.io/open-traffic-generator/ixia-c-traffic-engine:1.8.0.245
85 | kind load docker-image ghcr.io/open-traffic-generator/ixia-c-protocol-engine:1.00.0.488
86 | kind load docker-image ubuntu:22.04
87 | kind load docker-image networkop/meshnet:latest
88 | kind load docker-image networkop/init-wait:latest
89 |
90 | # make sure all intended images have been pushed correctly
91 | docker exec kind-control-plane crictl images
92 | ```
93 |
94 | 4. Setup meshnet
95 |
96 | ```bash
97 | # Clone and checkout tested version of meshnet
98 | git clone https://github.com/networkop/meshnet-cni
99 | cd meshnet-cni && git checkout f26c193 && cd -
100 |
101 | # Deploy meshnet
102 | kubectl apply -k meshnet-cni/manifests/base
103 |
104 | # wait for meshnet pods
105 | kubectl wait --for=condition=Ready pods -n meshnet --all
106 | ```
107 |
108 | 5. Create virtual interfaces and P2P link using meshnet
109 |
110 | The deployment file to be applied specifies name of test port pods and dut pod, interfaces to be created inside each of them and corresponding peer interface to create P2P link with.
111 |
112 | ```bash
113 | # create meshnet CRD
114 | kubectl apply -f deployments/k8s/network-emulation/namespace.yaml
115 | kubectl apply -f deployments/k8s/network-emulation/links.yaml
116 |
117 | # ensure meshnet CRD is successfully created (should list three entries)
118 | kubectl get topologies -A
119 | ```
120 |
121 | 6. Create Ixia-C / DUT pods and services
122 |
123 | ```bash
124 | # Create pods and services
125 | kubectl apply -f deployments/k8s/network-emulation/pods.yaml
126 | kubectl apply -f deployments/k8s/network-emulation/services.yaml
127 |
128 | # Wait for pods and list pods / services
129 | kubectl wait --for=condition=Ready pods -n ixia-c --all
130 | kubectl get pods -n ixia-c
131 | kubectl get services -n ixia-c
132 | ```
133 |
134 | 7. Run IPv4 forwarding test using [snappi](https://github.com/open-traffic-generator/snappi/tree/main/gosnappi) which is an auto-generated SDK based on [Open Traffic Generator API](https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/open-traffic-generator/models/master/artifacts/openapi.yaml&nocors)
135 |
136 | The test parameters, e.g. location of KENG controller, name of interfaces, etc. can be modified inside `testConst` map in `ipfwd.go`.
137 |
138 | Check the file for more details on the test.
139 |
140 | ```bash
141 | cd deployments/k8s/network-emulation/
142 | go run ipfwd.go
143 | cd -
144 | ```
145 |
146 | 8. (Optional) Cleanup
147 |
148 | ```bash
149 | # remove cluster if deployed using kind
150 | kind delete cluster
151 | sudo rm -rf /usr/local/bin/kubectl
152 |
153 | # remove pods / services / CRDs
154 | kubectl delete -f deployments/k8s/network-emulation/services.yaml
155 | kubectl delete -f deployments/k8s/network-emulation/pods.yaml
156 | kubectl delete -f deployments/k8s/network-emulation/links.yaml
157 | kubectl delete -f deployments/k8s/network-emulation/namespace.yaml
158 | ```
159 |
160 | ### What's Next ?
161 |
162 | - Checkout Ixia-C Release Page to get compatible client and server builds.
163 | * [All Releases](https://github.com/open-traffic-generator/ixia-c/releases)
164 | * [Latest Release](https://github.com/open-traffic-generator/ixia-c/releases/latest)
165 | - Checkout [OTG Conformance Tests](https://github.com/open-traffic-generator/conformance) for examples on various OTG features written in Go and Python.
166 | - Checkout [OTG Examples](https://github.com/open-traffic-generator/otg-examples) for more comprehensive examples / labs.
167 |
--------------------------------------------------------------------------------
/docs/deployments-kne.md:
--------------------------------------------------------------------------------
1 | # Deploy Ixia-c using KNE
2 |
3 | Ixia-c can be deployed in the k8s environment by using the [Kubernetes Network Emulation](https://github.com/openconfig/kne) that consists of the following services:
4 |
5 | * **operator**: Serves API request from the clients and manages workflow across one or more traffic engines.
6 | * **controller**: Serves API request from the clients and manages workflow across one or more traffic engines.
7 | * **traffic-engine**: Generates, captures, and processes the traffic from one or more network interfaces (on linux-based OS).
8 | * **protocol-engine**: Emulates layer3 networks and protocols such as BGP, ISIS, and etc (on linux-based OS).
9 | * **gnmi-server**: Captures statistics from one or more network interfaces (on linux-based OS).
10 |
11 | ## System Prerequisites
12 |
13 | ### CPU and RAM
14 |
15 | Following are the recommended resources for a basic use-case.
16 |
17 | - `keng-operator`: Each instance requires at least 1 CPU core and 2GB RAM.
18 | - `keng-controller`: Each instance requires at least 1 CPU core and 2GB RAM.
19 | - `otg-gnmi-server`: Each instance requires at least 1 CPU core and 2GB RAM.
20 | - `ixia-c-traffic-engine`: Each instance requires 2 dedicated CPU cores and 3GB dedicated RAM.
21 | - `ixia-c-protocol-engine`: Each instance requires 4 dedicated CPU cores and 1GB dedicated RAM per port.
22 |
23 | ### OS and Software Prerequisites
24 |
25 | - x86_64 Linux Distribution (Centos 7+ or Ubuntu 18+ have been tested)
26 | - Docker 19+ (as distributed by https://docs.docker.com/)
27 | - Go 1.17+
28 | - kind 0.18+
29 |
30 | ## Install KNE
31 |
32 | * The main use case we are interested in is the ability to bring up arbitrary topologies to represent a production topology. This would require multiple vendors as well as traffic generation and end hosts.
33 |
34 | ```sh
35 | go install github.com/openconfig/kne/kne@latest
36 | ```
37 |
38 | ## Deploy keng-operator
39 |
40 | * Ixia Operator defines CRD for Ixia network device (IxiaTG) and can be used to build up different network topologies with network devices from other vendors. Network interconnects between the topology nodes can be setup with various container network interface (CNI) plugins for Kubernetes for attaching multiple network interfaces to the nodes.
41 |
42 | ```sh
43 | kubectl apply -f https://github.com/open-traffic-generator/keng-operator/releases/download/v0.3.5/ixiatg-operator.yaml
44 | ```
45 |
46 | ## Apply configmap
47 |
48 | * The various Ixia component versions to be deployed is derived from the Ixia release version as specified in the IxiaTG config. These component mappings are captured in ixia-configmap.yaml for each Ixia release. The configmap, as shown in the snippet below, comprise of the Ixia release version ("release"), and the list of qualified component versions, for that release. Ixia Operator first tries to access these details from Keysight published releases; if unable to so, it tries to locate them in Kubernetes configmap. This allows users to have the operator load images from private repositories, by updating the configmap entries. Thus, for deployment with custom images, the user is expected to download release specific ixia-configmap.yaml from published releases. Then, in the configmap, update the specific container image "path" / "tag" fields and also update the "release" to some custom name. Start the operator first as specified in the deployment section below, before applying the configmap locally. After this the operator can be used to deploy the containers and services.
49 |
50 | * For community users,
51 |
52 | ```json
53 | apiVersion: v1
54 | kind: ConfigMap
55 | metadata:
56 | name: ixiatg-release-config
57 | namespace: ixiatg-op-system
58 | data:
59 | versions: |
60 | {
61 | "release": "1.42.0-4",
62 | "images": [
63 | {
64 | "name": "controller",
65 | "path": "ghcr.io/open-traffic-generator/keng-controller",
66 | "tag": "1.42.0-4"
67 | },
68 | {
69 | "name": "gnmi-server",
70 | "path": "ghcr.io/open-traffic-generator/otg-gnmi-server",
71 | "tag": "1.42.4"
72 | },
73 | {
74 | "name": "traffic-engine",
75 | "path": "ghcr.io/open-traffic-generator/ixia-c-traffic-engine",
76 | "tag": "1.8.0.245"
77 | },
78 | {
79 | "name": "protocol-engine",
80 | "path": "ghcr.io/open-traffic-generator/ixia-c-protocol-engine",
81 | "tag": "1.00.0.488"
82 | },
83 | {
84 | "name": "ixhw-server",
85 | "path": "ghcr.io/open-traffic-generator/keng-layer23-hw-server",
86 | "tag": "1.42.0-4"
87 | }
88 | ]
89 | }
90 | ```
91 |
92 | * For commercial users, `LICENSE_SERVERS` needs to be specified for `keng-controller` deployment.
93 |
94 | ```json
95 | apiVersion: v1
96 | kind: ConfigMap
97 | metadata:
98 | name: ixiatg-release-config
99 | namespace: ixiatg-op-system
100 | data:
101 | versions: |
102 | {
103 | "release": "1.42.0-4",
104 | "images": [
105 | {
106 | "name": "controller",
107 | "path": "ghcr.io/open-traffic-generator/keng-controller",
108 | "tag": "1.42.0-4",
109 | "env": {
110 | "LICENSE_SERVERS": "ip/hostname of license server"
111 | }
112 | },
113 | {
114 | "name": "gnmi-server",
115 | "path": "ghcr.io/open-traffic-generator/otg-gnmi-server",
116 | "tag": "1.42.4"
117 | },
118 | {
119 | "name": "traffic-engine",
120 | "path": "ghcr.io/open-traffic-generator/ixia-c-traffic-engine",
121 | "tag": "1.8.0.245"
122 | },
123 | {
124 | "name": "protocol-engine",
125 | "path": "ghcr.io/open-traffic-generator/ixia-c-protocol-engine",
126 | "tag": "1.00.0.488"
127 | },
128 | {
129 | "name": "ixhw-server",
130 | "path": "ghcr.io/open-traffic-generator/keng-layer23-hw-server",
131 | "tag": "1.42.0-4"
132 | }
133 | ]
134 | }
135 | ```
136 |
137 | ```sh
138 | # After saving the configmap snippet in a yaml file
139 | kubectl apply -f ixiatg-configmap.yaml
140 | ```
141 |
142 | ## Deploy the topology
143 |
144 | * The following snippet shows a simple KNE b2b topology.
145 |
146 | ```yaml
147 | name: ixia-c
148 | nodes:
149 | - name: otg
150 | vendor: KEYSIGHT
151 | version: 1.42.0-4
152 | services:
153 | 8443:
154 | name: https
155 | inside: 8443
156 | 40051:
157 | name: grpc
158 | inside: 40051
159 | 50051:
160 | name: gnmi
161 | inside: 50051
162 | links:
163 | - a_node: otg
164 | a_int: eth1
165 | z_node: otg
166 | z_int: eth2
167 | ```
168 |
169 | ```sh
170 | # After saving the topology snippet in a yaml file
171 | kne create topology.yaml
172 | ```
173 |
174 | * After deployment, you are now ready to run a test using this topology.
175 |
176 | ## Destroy/Remove the topology
177 |
178 | ```sh
179 | # delete a particular topology
180 | kne delete topology.yaml
181 | ```
182 |
--------------------------------------------------------------------------------
/deployments/k8s/network-emulation/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
2 | github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
3 | github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
4 | github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
5 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
6 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
8 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
9 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
10 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
11 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
12 | github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
13 | github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
14 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
15 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
16 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
17 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
18 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
19 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
20 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
21 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
22 | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
23 | github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
24 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
25 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
26 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
27 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
28 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
29 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
30 | github.com/open-traffic-generator/snappi/gosnappi v1.42.0 h1:T31pf1OCrIgBp2kR1j3xJzznLJxb2EQlR0M9vGyR+EY=
31 | github.com/open-traffic-generator/snappi/gosnappi v1.42.0/go.mod h1:sEsjIwvO/XpaL79kpU47mKZoxPc3M5ljm5MXySU9xs0=
32 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
33 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
34 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
35 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
36 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
37 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
38 | go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
39 | go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
40 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
41 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
42 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
43 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
44 | go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
45 | go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
46 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
47 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
48 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
49 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
50 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
51 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
52 | go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
53 | go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
54 | go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
55 | go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
56 | go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
57 | go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
58 | go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
59 | go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
60 | go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
61 | go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
62 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
63 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
64 | golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
65 | golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
66 | golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
67 | golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
68 | golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
69 | golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
70 | gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
71 | gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
72 | google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
73 | google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
74 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
75 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
76 | google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
77 | google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
78 | google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
79 | google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
80 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
81 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
82 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
83 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
84 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
85 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
86 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
87 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
88 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
89 |
--------------------------------------------------------------------------------
/docs/deployments.md:
--------------------------------------------------------------------------------
1 | # Deployment
2 |
3 | ## Overview
4 |
5 | Ixia-c is distributed and deployed as a multi-container application that consists of the following services:
6 |
7 | * **controller**: Serves API request from the clients and manages workflow across one or more traffic engines.
8 | * **traffic-engine**: Generates, captures, and processes traffic from one or more network interfaces (on linux-based OS).
9 | * **app-usage-reporter**: (Optional) Collects anonymous usage report from the controller and uploads it to the Keysight Cloud, with minimal impact on the host resources.
10 |
11 | All these services are available as docker images on the [GitHub Open-Traffic-Generator repository](https://github.com/orgs/open-traffic-generator/packages). To use specific versions of these images, see [Ixia-c Releases](releases.md) .
12 |
13 | 
14 |
15 | > Once the services are deployed, [conformance](https://github.com/open-traffic-generator/conformance/tree/main) (a collection of [snappi](https://pypi.org/project/snappi/) & [gosnappi](https://otg.dev/clients/gosnappi/) test scripts and configurations) can be setup to run against Ixia-c.
16 |
17 | ## Bootstrap
18 |
19 | The Ixia-c services can either all be deployed on the same host or each on separate hosts (as long as they are mutually reachable over the network). There is no boot-time dependency between them, which allows **horizontal scalability** without interrupting the existing services.
20 |
21 | You can establish a connectivity between the services in two ways. The options are as follows:
22 |
23 | - **controller & traffic-engine**: The client pushes a traffic configuration to the controller, containing the `location` of the traffic engine.
24 | - **controller & app-usage-reporter**: The Controller periodically tries to establish connectivity with the `app-usage-reporter` on a `location`, which can be overridden by using the controller's deployment parameters.
25 |
26 | >The **location** (network address) of the traffic-engine and the app-usage-reporter must be reachable from the controller, even if they are not reachable from the client scripts.
27 |
28 | ## Deployment types
29 |
30 | * [Using docker-compose](deployments-docker-compose.md)
31 |
32 | * [Using containerlab](deployments-containerlab.md)
33 |
34 | * [Using KNE](deployments-kne.md)
35 |
36 | ## Diagnostics
37 |
38 | Check and download controller logs:
39 |
40 | ```sh
41 | docker exec cat logs/controller.log
42 | # follow logs
43 | docker exec tail -f logs/controller.log
44 | # check stdout output
45 | docker logs
46 | # download logs
47 | docker cp :$(docker exec readlink -f logs/controller.log) ./
48 | ```
49 |
50 | Check and download traffic-engine logs:
51 |
52 | ```sh
53 | docker exec cat /var/log/usstream/usstream.log
54 | # follow logs
55 | docker exec tail -f /var/log/usstream/usstream.log
56 | # check stdout output
57 | docker logs
58 | # download logs
59 | docker cp :/var/log/usstream/usstream.log ./
60 | ```
61 |
62 | ## Test Suite
63 |
64 | ## One-arm Scenario
65 |
66 | > TODO: diagram
67 |
68 | * Automated
69 |
70 | ```bash
71 | docker-compose -f deployments/raw-one-arm.yml up -d # community users
72 | # optionally stop and remove services deployed
73 | docker-compose -f deployments/raw-one-arm.yml down # community users
74 | ```
75 |
76 | * Manual
77 |
78 | ```bash
79 | # start controller and app usage reporter
80 |
81 | # community users
82 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula
83 | # commercial users
84 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula --license-servers="ip/hostname of license server"
85 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-app-usage-reporter
86 |
87 | # start traffic engine on network interface eth1, TCP port 5555 and cpu cores 0, 1, 2
88 | docker run --net=host --privileged -d \
89 | -e OPT_LISTEN_PORT="5555" \
90 | -e ARG_IFACE_LIST="virtual@af_packet,eth1" \
91 | -e OPT_NO_HUGEPAGES="Yes" \
92 | --cpuset-cpus="0,1,2" \
93 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine
94 | ```
95 |
96 | ## Two-arm Scenario
97 |
98 | > TODO: diagram
99 |
100 | * Automated
101 |
102 | ```bash
103 | docker-compose -f deployments/raw-two-arm.yml up -d # community users
104 | # optionally stop and remove services deployed
105 | docker-compose -f deployments/raw-two-arm.yml down # community users
106 | ```
107 |
108 | * Manual
109 |
110 | ```bash
111 | # start controller and app usage reporter
112 | # community users
113 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula
114 | # commercial users
115 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula --license-servers="ip/hostname of license server"
116 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-app-usage-reporter
117 |
118 | # start traffic engine on network interface eth1, TCP port 5555 and cpu cores 0, 1, 2
119 | docker run --net=host --privileged -d \
120 | -e OPT_LISTEN_PORT="5555" \
121 | -e ARG_IFACE_LIST="virtual@af_packet,eth1" \
122 | -e OPT_NO_HUGEPAGES="Yes" \
123 | --cpuset-cpus="0,1,2" \
124 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine
125 |
126 | # start traffic engine on network interface eth2, TCP port 5556 and cpu cores 0, 3, 4
127 | docker run --net=host --privileged -d \
128 | -e OPT_LISTEN_PORT="5556" \
129 | -e ARG_IFACE_LIST="virtual@af_packet,eth2" \
130 | -e OPT_NO_HUGEPAGES="Yes" \
131 | --cpuset-cpus="0,3,4" \
132 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine
133 | ```
134 |
135 | ## Three-arm Mesh Scenario
136 |
137 | This scenario binds traffic engine to the management network interface, that belongs to the container which in turn is a part of the docker0 network.
138 |
139 | > TODO: diagram
140 |
141 | * Automated
142 |
143 | ```bash
144 | docker-compose -f deployments/raw-three-arm-mesh.yml up -d # community users
145 | # optionally stop and remove services deployed
146 | docker-compose -f deployments/raw-three-arm-mesh.yml down # community users
147 | ```
148 |
149 | * Manual
150 |
151 | ```bash
152 | # start controller and app usage reporter
153 | # community users
154 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula
155 | # commercial users
156 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-controller --accept-eula --license-servers="ip/hostname of license server"
157 | docker run --net=host -d ghcr.io/open-traffic-generator/keng-app-usage-reporter
158 |
159 | # start traffic engine on network interface eth0, TCP port 5555 and cpu cores 0, 1, 2
160 | docker run --privileged -d \
161 | -e OPT_LISTEN_PORT="5555" \
162 | -e ARG_IFACE_LIST="virtual@af_packet,eth0" \
163 | -e OPT_NO_HUGEPAGES="Yes" \
164 | -p 5555:5555 \
165 | --cpuset-cpus="0,1,2" \
166 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine
167 |
168 | # start traffic engine on network interface eth0, TCP port 5556 and cpu cores 0, 3, 4
169 | docker run --privileged -d \
170 | -e OPT_LISTEN_PORT="5555" \
171 | -e ARG_IFACE_LIST="virtual@af_packet,eth0" \
172 | -e OPT_NO_HUGEPAGES="Yes" \
173 | -p 5556:5555 \
174 | --cpuset-cpus="0,3,4" \
175 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine
176 |
177 | # start traffic engine on network interface eth0, TCP port 5557 and cpu cores 0, 5, 6
178 | docker run --privileged -d \
179 | -e OPT_LISTEN_PORT="5555" \
180 | -e ARG_IFACE_LIST="virtual@af_packet,eth0" \
181 | -e OPT_NO_HUGEPAGES="Yes" \
182 | -p 5557:5555 \
183 | --cpuset-cpus="0,5,6" \
184 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine
185 | ```
186 |
187 | ### TODO: Multi-port per TE container
188 |
189 | ## Tests
190 |
191 | Please follow [readme](https://github.com/open-traffic-generator/conformance/blob/main/readme.md) to run sample go/python tests.
192 |
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | - [Table of Contents](readme.md)
4 | - FAQ
5 | - [Ixia-c, Open Traffic Generator and snappi](#ixia-c-open-traffic-generator-and-snappi)
6 | - [Diagnostics](#diagnostics)
7 | - [Packets](#packets)
8 | - [Application Usage Reporter](#application-usage-reporter)
9 | - [Ixia-c Free Version](#Ixia-c-free-version)
10 | - [Troubleshooting](#troubleshooting)
11 | - [Support](#support)
12 |
13 | ## Ixia-c, Open Traffic Generator and snappi
14 |
15 |
16 |
17 | What is the relationship between Open Traffic Generator data models, snappi and Ixia-c?
18 |
19 |
20 |
21 | The [Open Traffic Generator](https://github.com/open-traffic-generator/models) data models describe a vendor neutral data models and APIs for test traffic generation. The models are based on OpenAPI v3. Ixia-c is a traffic generator that complies with these APIs. [snappi](https://github.com/open-traffic-generator/snappi) is a *Pythonic* client library that can be used to write tests that run against Ixia-c.
22 |
23 | The following two diagrams illustrate this relationship.
24 |
25 |
26 |
27 |

28 |
29 |
30 |
31 |
32 |

33 |
34 |
35 |
36 |
37 |
38 |
39 | Where can I find a tutorial on snappi?
40 |
41 |
42 |
43 | The [Hello snappi](hello-snappi.md) tutorial is a good starting point to get familiar with `snappi`.
44 |
45 |
46 |
47 |
48 | How to use other non-Python clients?
49 |
50 |
51 |
52 | TODO: Fill in details here.
53 |
54 |
55 |
56 |
57 | Can I use curl to control Ixia-c?
58 |
59 |
60 |
61 | Yes. Refer to the [Quick Start](../readme.md##quick-start) on the Ixia-c home page.
62 |
63 |
64 | ## Diagnostics
65 |
66 |
67 |
68 | What version of the Open Traffic Generator spec does Ixia-c implement?
69 |
70 |
71 |
72 | Ixia-c implements version **[v0.12.5](https://github.com/open-traffic-generator/models/releases/tag/v0.12.5)** of the Open Traffic Generator Data Model. You can view the model [here](https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/open-traffic-generator/models/v0.12.5/artifacts/openapi.yaml).
73 |
74 |
75 |
76 |
77 |
78 | How to find version of Open Traffic Generator spec implemented by Ixia-c?
79 |
80 |
81 |
82 | Open Traffic Generator Data Model can be accessed from any browser by pointing it to (https://\/docs/openapi.json). The `info` section contains the `version` of the Open Traffic Generator Data Model implemented by the KENG controller.
83 |
84 |
85 |
86 | ## Packets
87 |
88 |
89 |
90 | What do packets look like?
91 |
92 |
93 |
94 | Ixia packet testers utilize a proprietary flow-tracking technique which involves inserting a special *instrumentation header* into the packet. It is inserted after the last valid protocol header ie, in the payload.
95 |
96 |
97 |
98 |

99 |
100 |
101 |
102 | The fields in this header are:
103 |
104 | - a *signature* which servers as marker to indicate start of header
105 | - a *PGID* or *port group id* field to distinguish between different flows
106 | - a 32 bit *sequence number* that can be used to detect packet re-ordering
107 | - a 32 bit timestamp that can be used to measure one-way latency
108 |
109 |
110 |
111 |
112 |
113 | How do I disable packet instrumentation?
114 |
115 |
116 |
117 | According to the Open Traffic Generator data model packet instrumentation is enabled by default on all the flows. It is not possible to disable it at the moment. An update to the data model that allows the end user to disable instrumentation is in the process of being merged into the *main* branch and will be submitted in the coming weeks. Ixia-c will implement it at that point.
118 |
119 |
120 |
121 |
122 |
123 | Does Ixia-c calculate packet checksums? If so, which ones?
124 |
125 |
126 |
127 | Yes, the Ixia-c traffic-engine automatically calculates the Ethernet FCS, IPv4, TCP and UDP checksums. The traffic-engine is capable of calculating *four* checksums (in each packet). TODO: verify.
128 |
129 |
130 |
131 |
132 | What layer1 settings does Ixia-c utilize?
133 |
134 |
135 |
136 | The only `layer1` setting that is used by the Ixia-c traffic engine is `speed`. This setting is used to help convert flow rate specified as *percentage line-rate* into packets/second (pps) and to ensure that proper inter-packet-gaps are used.
137 |
138 |
139 |
140 |
141 | What is the purpose of the layer1 setting speed?
142 |
143 |
144 |
145 | To calculate packets/second (pps) when the flow rate is specified as *percentage line-rate* and to ensure proper inter-packet-gaps.
146 |
147 |
148 | ## Application Usage Reporter
149 |
150 |
151 |
152 | What is Application Usage Reporter?
153 |
154 |
155 |
156 | The `app-usage-reporter` container collects and uploads to the Keysight cloud some basic telemetry information from the KENG controller. This information helps Keysight improve the controller by focusing on the features that are being used by end users.
157 |
158 |
159 |
160 |
161 | What information does Application Usage Reporter collect?
162 |
163 |
164 |
165 | The *Application Usage Reporter* collects configuration related information like the number of ports in a test, number of flows configured, number of values used in a pattern. TODO: Need to give an exhaustive list.
166 |
167 |
168 |
169 |
170 | Does Application Usage Reporter collect any personally identifiable information?
171 |
172 |
173 |
174 | No. The *Application Usage Reporter* does NOT collect any personally identifiable information like username, hostname, email address, etc.
175 |
176 |
177 |
178 |
179 | Does Application Usage Reporter save my IP address?
180 |
181 |
182 | No.
183 |
184 |
185 |
186 |
187 | Can I disable Application Usage Reporter? If so, how?
188 |
189 |
190 |
191 | Yes, the *Application Usage Reporter* can be disabled. Refer to the [Deployment Parameters](deployments.md#deployment-parameters) section in the Deployment Guide.
192 |
193 |
194 | ## Ixia-c Free Version
195 |
196 |
197 |
198 | What are the limitations of the free version of Ixia-c?
199 |
200 |
201 |
202 | The free version of KENG controller supports up to 4 ports in one session and the Ixia-c traffic-engine is limited to running over `raw` sockets.
203 |
204 |
205 |
206 |
207 | What are the differences between the commercial and the free version of Ixia-c?
208 |
209 |
210 | The following table highlights the differences between the commercial and the free version of Ixia-c.
211 |
212 | | Capability | Ixia-c Free | Ixia-c Commercial |
213 | -------------| ------------| ------------------|
214 | | # of Ports | 4 | Unlimited |
215 | | Traffic Engine - Raw | Supported | Supported |
216 | | Traffic Engine - DPDK PCI | Not Supported | Supported |
217 | | Support | Slack Channel | Keysight Support |
218 |
219 |
220 |
221 |
222 |
223 | How do I purchase the commercial version of Ixia-c?
224 |
225 |
226 |
227 | Contact your Keysight Sales Rep or reach out to us [here](https://www.keysight.com/us/en/contact.html).
228 |
229 |
230 | ## Troubleshooting
231 |
232 |
233 |
234 | How do I view KENG controller logs?
235 |
236 |
237 |
238 | Use `docker logs` to view the controller log.
239 |
240 |
241 |
242 |
243 | What is the message "App usage reporting service is down" in KENG controller log?
244 |
245 |
246 |
247 | This message indicates that the `app-usage-reporter` container is not reachable from the KENG controller. This does NOT affect KENG controller's normal operation. Refer to [Deployment Parameters](deployments.md#deployment-parameters) for more details on how to override the default location for the app-usage-reporter or how to disable it all together.
248 |
249 |
250 | ## Support
251 |
252 |
253 |
254 | Where do I get support for the free version of Ixia-c?
255 |
256 |
257 |
258 | Reach out to us on [Slack](support.md) for support.
259 |
260 |
--------------------------------------------------------------------------------
/deployments/docker/readme.md:
--------------------------------------------------------------------------------
1 | # Ixia-C Docker Deployment over Raw Socket
2 |
3 | ## Manual Steps
4 |
5 | 1. (Optional) Cleanup all existing containers and images.
6 |
7 | ```sh
8 | docker stop $(docker ps -aq) && docker rm $(docker ps -aq)
9 | docker rmi $(docker images -q)
10 | ```
11 |
12 | 2. Start an instance of `controller`.
13 |
14 | ```sh
15 | # implicitly listens on TCP port 8443 and 40051
16 | # --accept-eula option is confirmation that user has accepted the Ixia-C End-User License Agreement (EULA)
17 | # located at https://github.com/open-traffic-generator/ixia-c/blob/main/docs/eula.md
18 | docker run -d \
19 | --name=keng-controller \
20 | --publish 0.0.0.0:8443:8443 \
21 | --publish 0.0.0.0:40051:40051 \
22 | ghcr.io/open-traffic-generator/keng-controller:1.42.0-4 \
23 | --accept-eula \
24 | --trace \
25 | --disable-app-usage-reporter
26 | ```
27 |
28 |
29 | 3. Start one or more instances of `traffic-engine`.
30 |
31 | - For `traffic-engine` over raw socket:
32 |
33 | ```sh
34 | # for network interface eth1
35 | docker run --privileged -d \
36 | --name=ixia-c-traffic-engine-eth1 \
37 | -e OPT_LISTEN_PORT="5555" \
38 | -e ARG_IFACE_LIST="virtual@af_packet,eth1" \
39 | -e OPT_NO_HUGEPAGES="Yes" \
40 | -e OPT_NO_PINNING="Yes" \
41 | -e WAIT_FOR_IFACE="Yes" \
42 | -e OPT_ADAPTIVE_CPU_USAGE="Yes" \
43 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine:1.8.0.245
44 |
45 | # for network interface eth2
46 | docker run --privileged -d \
47 | --name=ixia-c-traffic-engine-eth2 \
48 | -e OPT_LISTEN_PORT="5555" \
49 | -e ARG_IFACE_LIST="virtual@af_packet,eth2" \
50 | -e OPT_NO_HUGEPAGES="Yes" \
51 | -e OPT_NO_PINNING="Yes" \
52 | -e WAIT_FOR_IFACE="Yes" \
53 | -e OPT_ADAPTIVE_CPU_USAGE="Yes" \
54 | ghcr.io/open-traffic-generator/ixia-c-traffic-engine:1.8.0.245
55 | ```
56 |
57 | 4. Start one or more instances of `protocol-engine`.
58 |
59 | - For `protocol-engine` over raw socket:
60 |
61 | ```sh
62 | # for network interface eth1
63 | docker run --privileged -d \
64 | --net=container:ixia-c-traffic-engine-eth1 \
65 | --name=ixia-c-protocol-engine-eth1 \
66 | -e INTF_LIST="eth1" \
67 | ghcr.io/open-traffic-generator/ixia-c-protocol-engine:1.00.0.488 \
68 |
69 | # for network interface eth2
70 | docker run --privileged -d \
71 | --net=container:ixia-c-traffic-engine-eth2 \
72 | --name=ixia-c-protocol-engine-eth2 \
73 | -e INTF_LIST="eth2" \
74 | ghcr.io/open-traffic-generator/ixia-c-protocol-engine:1.00.0.488
75 | ```
76 |
77 | 5. Ensure existing network interfaces are `Up` and have `Promiscuous` mode enabled.
78 |
79 | ```sh
80 | # check interface details
81 | sudo ip addr
82 | # configure as required
83 | sudo ip link set eth1 up
84 | sudo ip link set eth1 promisc on
85 | sudo ip link set eth2 up
86 | sudo ip link set eth2 promisc on
87 | ```
88 |
89 | 6. Push interface to the containers.
90 | - It takes a host NIC (say eth1) and injects it into a container’s network namespace so the container can directly use that NIC (bypassing Docker’s default bridge).
91 | It symlinks the container’s netns into /var/run/netns, then moves and configures the interface inside that namespace.
92 |
93 | ```sh
94 |
95 | # For Traffic Engine 1
96 | # Resolve container metadata
97 | cid=$(docker inspect --format="{{json .Id}}" ixia-c-traffic-engine-eth1 | cut -d\" -f 2)
98 | cpid=$(docker inspect --format="{{json .State.Pid}}" ixia-c-traffic-engine-eth1 | cut -d\" -f 2)
99 |
100 | # Prepare namespace paths
101 | orgPath=/proc/${cpid}/ns/net
102 | newPath=/var/run/netns/${cid}
103 |
104 | # Make namespace accessible to ip netns
105 | sudo mkdir -p /var/run/netns
106 | sudo ln -s ${orgPath} ${newPath}
107 | # Move interface into the container’s netns
108 | sudo ip link set eth1 netns ${cid}
109 | # Rename and configure inside the container
110 | sudo ip netns exec ${cid} ip link set eth1 name eth1
111 | sudo ip netns exec ${cid} ip -4 addr add 0/0 dev eth1
112 | sudo ip netns exec ${cid} ip -4 link set eth1 up
113 |
114 | # For Traffic Engine 2
115 | # Resolve container metadata
116 | cid=$(docker inspect --format="{{json .Id}}" ixia-c-traffic-engine-eth2 | cut -d\" -f 2)
117 | cpid=$(docker inspect --format="{{json .State.Pid}}" ixia-c-traffic-engine-eth2 | cut -d\" -f 2)
118 |
119 | # Prepare namespace paths
120 | orgPath=/proc/${cpid}/ns/net
121 | newPath=/var/run/netns/${cid}
122 |
123 | # Make namespace accessible to ip netns
124 | sudo mkdir -p /var/run/netns
125 | sudo ln -s ${orgPath} ${newPath}
126 | # Move interface into the container’s netns
127 | sudo ip link set eth2 netns ${cid}
128 | # Rename and configure inside the container
129 | sudo ip netns exec ${cid} ip link set eth2 name eth2
130 | sudo ip netns exec ${cid} ip -4 addr add 0/0 dev eth2
131 | sudo ip netns exec ${cid} ip -4 link set eth2 up
132 | ```
133 |
134 | 6. Create interface-port config-map inside `keng-controller`.
135 | ```sh
136 | configDir=/home/ixia-c/controller/config
137 | OTG_PORTA=$(docker inspect --format="{{json .NetworkSettings.IPAddress}}" ixia-c-traffic-engine-eth1 | cut -d\" -f 2)
138 | OTG_PORTZ=$(docker inspect --format="{{json .NetworkSettings.IPAddress}}" ixia-c-traffic-engine-eth2 | cut -d\" -f 2)
139 |
140 | yml="location_map:
141 | - location: ${ETH_A}
142 | endpoint: \"${OTG_PORTA}:5555+${OTG_PORTA}:50071\"
143 | - location: ${ETH_Z}
144 | endpoint: \"${OTG_PORTZ}:5555+${OTG_PORTZ}:50071\"
145 | "
146 | echo -n "$yml" | sed "s/^ //g" | tee ./config.yaml > /dev/null \
147 | && docker exec keng-controller mkdir -p ${configDir} \
148 | && docker cp ./config.yaml keng-controller:${configDir}/ \
149 | && rm -rf ./config.yaml
150 | ```
151 |
152 | ## Automated Steps
153 | ```sh
154 | git clone --recurse-submodule https://github.com/open-traffic-generator/ixia-c.git
155 | cd ixia-c/deployments/docker
156 | chmod u+x ./deployment.sh
157 |
158 | # deploy control & data plane
159 | ./deployment.sh eth1 eth2 topo new
160 |
161 | # teardown control & data plane
162 | ./deployment.sh eth1 eth2 topo rm
163 |
164 | # deploy data plane only
165 | DATA_PLANE_ONLY=true ./deployment.sh eth1 eth2 topo new
166 |
167 | # teardown data plane only
168 | DATA_PLANE_ONLY=true ./deployment.sh eth1 eth2 topo rm
169 |
170 | #create veth pair [ if needed ]
171 | ./deployment.sh veth1 veth2 create
172 | ```
173 |
174 |
175 | ## Sample go snippet to use after deployment for testing
176 | ```go
177 | // Create a new API handle to make API calls against OTG
178 | api := gosnappi.NewApi()
179 |
180 | // Set the transport protocol to HTTP
181 | api.NewHttpTransport().SetLocation("https://localhost:8443")
182 |
183 | // Create a new traffic configuration that will be set on OTG
184 | config := gosnappi.NewConfig()
185 |
186 | // Add a test port to the configuration
187 | ptx := config.Ports().Add().SetName("ptx").SetLocation("eth1")
188 | prx := config.Ports().Add().SetName("prx").SetLocation("eth2")
189 | ```
190 |
191 | ## Example Go test
192 | - Setup
193 | ```bash
194 | git clone --recurse-submodule https://github.com/open-traffic-generator/conformance.git
195 | cd conformance
196 | go mod tidy
197 | go mod download
198 | ```
199 | - Test for control & data plane
200 | ```bash
201 | go test -tags=all ./examples -run TestQuickstartB2BCpDp -args -portA=eth1 -portZ=eth2
202 | ```
203 | - Test for data plane only
204 | ```bash
205 | go test -tags=all ./examples -run TestQuickstartB2BDp -args -portA=eth1 -portZ=eth2
206 | ```
207 |
208 |
209 |
210 |
211 |
212 |
213 |
--------------------------------------------------------------------------------
/deployments/docker/deployment.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 |
5 | VERSIONS_YAML_LOC="https://github.com/open-traffic-generator/ixia-c/releases/download/v1.42.0-4/versions.yaml"
6 | VERSIONS_YAML="versions.yaml"
7 | CTRL_IMAGE="ghcr.io/open-traffic-generator/keng-controller"
8 | TE_IMAGE="ghcr.io/open-traffic-generator/ixia-c-traffic-engine"
9 | PE_IMAGE="ghcr.io/open-traffic-generator/ixia-c-protocol-engine"
10 |
11 | if [ -n "$AUTH_TOKEN" ]; then
12 | curl -lO -H "Authorization: Bearer $AUTH_TOKEN" $VERSIONS_YAML_LOC
13 | else
14 | curl -kLO $VERSIONS_YAML_LOC
15 | fi
16 |
17 | TIMEOUT_SECONDS=300
18 |
19 | # --- Argument parsing for ETH_A and ETH_Z ---
20 | if [ -z "$1" ] || [ -z "$2" ]; then
21 | echo "usage: $0 [args...]"
22 | exit 1
23 | fi
24 |
25 | ETH_A=$1
26 | ETH_Z=$2
27 | shift 2
28 |
29 | set_docker_permission() {
30 | if ! groups $USER | grep -q '\bdocker\b'; then
31 | echo "Adding $USER to docker group (relogin required to take effect)."
32 | sudo usermod -aG docker $USER
33 | fi
34 | docker ps -a
35 | }
36 |
37 | set_docker_permission
38 |
39 | configq() {
40 | # echo is needed to further evaluate the
41 | # contents extracted from configuration
42 | eval echo $(yq "${@}" versions.yaml)
43 | }
44 |
45 | push_ifc_to_container() {
46 | # It takes a host NIC (say eth1) and injects it into a container’s
47 | # network namespace so the container can directly use that NIC (bypassing Docker’s default bridge).
48 | # It symlinks the container’s netns into /var/run/netns,
49 | # then moves and configures the interface inside that namespace.
50 | if [ -z "${1}" ] || [ -z "${2}" ]
51 | then
52 | echo "usage: ${0} push_ifc_to_container "
53 | exit 1
54 | fi
55 |
56 | # Resolve container metadata
57 | cid=$(container_id ${2})
58 | cpid=$(container_pid ${2})
59 |
60 | echo "Changing namespace of ifc ${1} to container ID ${cid} pid ${cpid}"
61 |
62 | # Prepare namespace paths
63 | orgPath=/proc/${cpid}/ns/net
64 | newPath=/var/run/netns/${cid}
65 |
66 | # Make namespace accessible to ip netns
67 | # Move interface into the container’s netns
68 | # Rename and configure inside the container
69 | sudo mkdir -p /var/run/netns
70 | echo "Creating symlink ${orgPath} -> ${newPath}"
71 | sudo ln -s ${orgPath} ${newPath} \
72 | && sudo ip link set ${1} netns ${cid} \
73 | && sudo ip netns exec ${cid} ip link set ${1} name ${1} \
74 | && sudo ip netns exec ${cid} ip -4 addr add 0/0 dev ${1} \
75 | && sudo ip netns exec ${cid} ip -4 link set ${1} up \
76 | && echo "Successfully changed namespace of ifc ${1}"
77 |
78 | sudo rm -rf ${newPath}
79 | }
80 |
81 | container_id() {
82 | docker inspect --format="{{json .Id}}" ${1} | cut -d\" -f 2
83 | }
84 |
85 | container_pid() {
86 | docker inspect --format="{{json .State.Pid}}" ${1} | cut -d\" -f 2
87 | }
88 |
89 | container_ip() {
90 | docker inspect --format="{{json .NetworkSettings.IPAddress}}" ${1} | cut -d\" -f 2
91 | }
92 |
93 | ixia_c_img_tag() {
94 | tag=$(grep ${1} ${VERSIONS_YAML} | cut -d: -f2 | cut -d\ -f2)
95 | echo "${tag}"
96 | }
97 |
98 | ixia_c_traffic_engine_img() {
99 | echo "${TE_IMAGE}:$(ixia_c_img_tag ixia-c-traffic-engine)"
100 | }
101 |
102 | ixia_c_protocol_engine_img() {
103 | echo "${PE_IMAGE}:$(ixia_c_img_tag ixia-c-protocol-engine)"
104 | }
105 |
106 | keng_controller_img() {
107 | echo "${CTRL_IMAGE}:$(ixia_c_img_tag keng-controller)"
108 | }
109 |
110 | gen_controller_config_b2b_cpdp() {
111 | configdir=/home/ixia-c/controller/config
112 | OTG_PORTA=$(container_ip ixia-c-traffic-engine-${ETH_A})
113 | OTG_PORTZ=$(container_ip ixia-c-traffic-engine-${ETH_Z})
114 |
115 | wait_for_sock ${OTG_PORTA} 5555
116 | wait_for_sock ${OTG_PORTA} 50071
117 | wait_for_sock ${OTG_PORTZ} 5555
118 | wait_for_sock ${OTG_PORTZ} 50071
119 |
120 | yml="location_map:
121 | - location: ${ETH_A}
122 | endpoint: \"${OTG_PORTA}:5555+${OTG_PORTA}:50071\"
123 | - location: ${ETH_Z}
124 | endpoint: \"${OTG_PORTZ}:5555+${OTG_PORTZ}:50071\"
125 | "
126 | echo -n "$yml" | sed "s/^ //g" | tee ./config.yaml > /dev/null \
127 | && docker exec keng-controller mkdir -p ${configdir} \
128 | && docker cp ./config.yaml keng-controller:${configdir}/ \
129 | && rm -rf ./config.yaml
130 | }
131 |
132 | gen_controller_config_b2b_dp() {
133 | configdir=/home/ixia-c/controller/config
134 | OTG_PORTA=$(container_ip ixia-c-traffic-engine-${ETH_A})
135 | OTG_PORTZ=$(container_ip ixia-c-traffic-engine-${ETH_Z})
136 |
137 | wait_for_sock ${OTG_PORTA} 5555
138 | wait_for_sock ${OTG_PORTZ} 5555
139 |
140 | yml="location_map:
141 | - location: ${ETH_A}
142 | endpoint: \"${OTG_PORTA}:5555\"
143 | - location: ${ETH_Z}
144 | endpoint: \"${OTG_PORTZ}:5555\"
145 | "
146 | echo -n "$yml" | sed "s/^ //g" | tee ./config.yaml > /dev/null \
147 | && docker exec keng-controller mkdir -p ${configdir} \
148 | && docker cp ./config.yaml keng-controller:${configdir}/ \
149 | && rm -rf ./config.yaml
150 | }
151 |
152 | wait_for_sock() {
153 | TIMEOUT_SECONDS=30
154 | if [ ! -z "${3}" ]
155 | then
156 | TIMEOUT_SECONDS=${3}
157 | fi
158 | echo "Waiting for ${1}:${2} to be ready (timeout=${TIMEOUT_SECONDS}s)..."
159 | elapsed=0
160 | TIMEOUT_SECONDS=$(($TIMEOUT_SECONDS * 10))
161 | while true
162 | do
163 | nc -z -v ${1} ${2} && return 0
164 |
165 | elapsed=$(($elapsed+1))
166 | # echo "Timeout: $TIMEOUT_SECONDS"
167 | # echo "elapsed time: $elapsed"
168 |
169 | if [ $elapsed -gt ${TIMEOUT_SECONDS} ]
170 | then
171 | echo "${1}:${2} to be ready after ${TIMEOUT_SECONDS}"
172 | exit 1
173 | fi
174 | sleep 0.1
175 | done
176 |
177 | }
178 |
179 | prepare_eth_pair() {
180 | if [ -z "${1}" ] || [ -z "${2}" ]
181 | then
182 | echo "usage: ${0} create_veth_pair "
183 | exit 1
184 | fi
185 |
186 | sudo ip link set ${1} up \
187 | && sudo ip link set ${1} promisc on \
188 | && sudo ip link set ${2} up \
189 | && sudo ip link set ${2} promisc on
190 | }
191 |
192 | create_ixia_c_b2b_cpdp() {
193 | docker ps -a
194 | echo "Setting up back-to-back with CP/DP distribution of ixia-c ..."
195 | docker run -d \
196 | --name=keng-controller \
197 | --publish 0.0.0.0:8443:8443 \
198 | --publish 0.0.0.0:40051:40051 \
199 | $(keng_controller_img) \
200 | --accept-eula \
201 | --trace \
202 | --disable-app-usage-reporter
203 | docker run --privileged -d \
204 | --name=ixia-c-traffic-engine-${ETH_A} \
205 | -e OPT_LISTEN_PORT="5555" \
206 | -e ARG_IFACE_LIST="virtual@af_packet,${ETH_A}" \
207 | -e OPT_NO_HUGEPAGES="Yes" \
208 | -e OPT_NO_PINNING="Yes" \
209 | -e WAIT_FOR_IFACE="Yes" \
210 | -e OPT_ADAPTIVE_CPU_USAGE="Yes" \
211 | $(ixia_c_traffic_engine_img)
212 | if [ -z "${DATA_PLANE_ONLY}" ]
213 | then
214 | docker run --privileged -d \
215 | --net=container:ixia-c-traffic-engine-${ETH_A} \
216 | --name=ixia-c-protocol-engine-${ETH_A} \
217 | -e INTF_LIST="${ETH_A}" \
218 | $(ixia_c_protocol_engine_img)
219 | fi
220 | docker run --privileged -d \
221 | --name=ixia-c-traffic-engine-${ETH_Z} \
222 | -e OPT_LISTEN_PORT="5555" \
223 | -e ARG_IFACE_LIST="virtual@af_packet,${ETH_Z}" \
224 | -e OPT_NO_HUGEPAGES="Yes" \
225 | -e OPT_NO_PINNING="Yes" \
226 | -e WAIT_FOR_IFACE="Yes" \
227 | -e OPT_ADAPTIVE_CPU_USAGE="Yes" \
228 | $(ixia_c_traffic_engine_img)
229 | if [ -z "${DATA_PLANE_ONLY}" ]
230 | then
231 | docker run --privileged -d \
232 | --net=container:ixia-c-traffic-engine-${ETH_Z} \
233 | --name=ixia-c-protocol-engine-${ETH_Z} \
234 | -e INTF_LIST="${ETH_Z}" \
235 | $(ixia_c_protocol_engine_img)
236 | fi
237 | docker ps -a \
238 | && prepare_eth_pair ${ETH_A} ${ETH_Z} \
239 | && push_ifc_to_container ${ETH_A} ixia-c-traffic-engine-${ETH_A} \
240 | && push_ifc_to_container ${ETH_Z} ixia-c-traffic-engine-${ETH_Z}
241 | if [ -z "${DATA_PLANE_ONLY}" ]
242 | then
243 | gen_controller_config_b2b_cpdp $1
244 | else
245 | gen_controller_config_b2b_dp $1
246 | fi
247 | docker ps -a
248 | echo "Successfully deployed !"
249 | }
250 |
251 | rm_ixia_c_b2b_cpdp() {
252 | docker ps -a
253 | echo "Tearing down back-to-back with CP/DP distribution of ixia-c ..."
254 | docker stop keng-controller && docker rm keng-controller
255 |
256 | docker stop ixia-c-traffic-engine-${ETH_A}
257 | docker rm ixia-c-traffic-engine-${ETH_A}
258 | docker stop ixia-c-traffic-engine-${ETH_Z}
259 | docker rm ixia-c-traffic-engine-${ETH_Z}
260 |
261 | if [ -z "${DATA_PLANE_ONLY}" ]
262 | then
263 | docker stop ixia-c-protocol-engine-${ETH_A}
264 | docker rm ixia-c-protocol-engine-${ETH_A}
265 | docker stop ixia-c-protocol-engine-${ETH_Z}
266 | docker rm ixia-c-protocol-engine-${ETH_Z}
267 | fi
268 | docker ps -a
269 | }
270 |
271 | create() {
272 | sudo ip link add $ETH_A type veth peer name $ETH_Z
273 | sudo ip link set $ETH_A up
274 | sudo ip link set $ETH_Z up
275 | }
276 |
277 | topo() {
278 | case $1 in
279 | new )
280 | create_ixia_c_b2b_cpdp
281 | ;;
282 | rm )
283 | rm_ixia_c_b2b_cpdp
284 | ;;
285 | * )
286 | exit 1
287 | ;;
288 | esac
289 | }
290 |
291 |
292 | help() {
293 | grep "() {" ${0} | cut -d\ -f1
294 | }
295 |
296 | usage() {
297 | echo "usage: $0 [name of any function in script]"
298 | exit 1
299 | }
300 |
301 | case $1 in
302 | * )
303 | cmd=${1}
304 | echo "Hi"
305 | shift 1
306 | ${cmd} "$@" || usage
307 | ;;
308 | esac
309 |
--------------------------------------------------------------------------------
/docs/reference/licensing.md:
--------------------------------------------------------------------------------
1 | # Licensing
2 |
3 | ## License consumption mechanism and feature licenses
4 |
5 | Elastic Network Generator [licenses](../licensing.md) include the following features which depends on the license edition. Details on how the features are consumed are as follows:
6 |
7 | ### Feature Licenses
8 |
9 | | Feature Licenses | Developer | Team | System |
10 | |-------------------------------------|----------------------|-----------------|------------------------|
11 | | KENG-SEAT | 1 | 8 | 16 |
12 | | KENG-SEAT-UHD | N/A | 8 | 16 |
13 | | KENG-SEAT-IXHW | N/A | N/A | 16 |
14 | | KENG-DPLU | 50 | 400 | 800 |
15 | | KENG-CPLU | 50 | 400 | 800 |
16 | | KENG-UNLIMITED-CP | N/A | N/A | 16 |
17 |
18 | The exact list of feature licenses that are required by a specific test configuration, is calculated based on the test port type, port speed, protocol, protocol sessions, and etc. Overall, the list of required licenses is referred to as Test Cost.
19 |
20 | ### Test Cost Calculation
21 |
22 | ```
23 | Test Cost = Seat Cost + CP Cost * KENG-CPLU + DP Cost * KENG-DPLU
24 | ```
25 |
26 | | Port Type | Condition | Seat Cost | CP Cost | DP cost |
27 | |------------------|------------------- |-----------------------------------------------------------------------|------------------------- |--------------- |
28 | | Ixia-c SW | `If CP Cost <= 50` | `1x KENG-SEAT` | `SUM (Protocol Cost)` | `SUM (Speed Cost)` |
29 | | Ixia-c SW | `If CP Cost > 50`3 | `1x KENG-SEAT`
`1x KENG-UNLIMITED-CP` | `50` | `SUM (Speed Cost)` |
30 | | UHD400T | `If CP Cost <= 50` | `1x KENG-SEAT`
`1x KENG-SEAT-UHD` | `SUM (Protocol Cost)` | `0` |
31 | | UHD400T | `If CP Cost > 50`3 | `1x KENG-SEAT`
`1x KENG-SEAT-UHD`
`1x KENG-UNLIMITED-CP` | `50` | `0` |
32 | | IxOS Hardware | | `1x KENG-SEAT`
`1x KENG-SEAT-IXHW` | `0` | `0` |
33 |
34 | - **Seat** is the number of the running `keng-controller` instances, with a configuration that exceeds the capabilities of the Community Edition.
35 | - **The Data Plane License Unit** (`KENG-DPLU`) is associated with the traffic port capacity.
36 | The number of required units is determined as a sum of the configured port speeds (1, 10, 25, 40, 50, 100GE). The maximum port performance might be less than the configured port speed.
37 | - **The Control Plane License unit** (`KENG-CPLU`) is associated with the control plane protocol scale. The number of required CP units is determined as a sum of the configured protocol sessions.
38 | - If `KENG-UNLIMITED-CP` is not available, an exact number of `KENG-CPLU` will be consumed.
39 | - See [Control Plane Cost](#control-plane-cost) for the `Protocol Cost` and [Data Plane Cost](#data-plane-cost) for the `Speed Cost`.
40 |
41 | ### Control Plane Cost
42 |
43 | Applies only to the Ixia-c software and UHD400T ports.
44 |
45 | ```
46 | CP Cost = For each Port: SUM (Protocol Cost)
47 | ```
48 |
49 | | Protocol | Session Definition | Protocol Cost/Session | Comment |
50 | |------------------------------|----------------------------------------- |-----------------------|----------------------- |
51 | | IP Interface (ARP, ND) | devices:
- ethernets:
- ipv4_addresses:
- ipv6_addresses: | 0 | |
52 | | IP Loopbacks | devices:
- ipv4_loopbacks:
- ipv6_loopbacks: | 0 | |
53 | | LLDP | lldp:
- connection:
- port_name: | 1 | Session = Test Port with LLDP enabled |
54 | | LACP | lacp:
- ports:
- port_name:
lacp: | 1 | Session = LAG group, no matter group size |
55 | | BGP | devices:
- bgp:
- ipv4_addresses:
- ipv6_addresses:
- peers: | 1 | Session = BGP peer |
56 | | ISIS | devices:
- isis:
- interfaces:
- eth_name: | 1 | Session = ISIS interface |
57 | | RSVP | devices:
- rsvp:
- ipv4_interfaces:
- neighbor_ip: | 1 | Session = RSVP neighbor |
58 |
59 | ### Data Plane Cost
60 |
61 | Applies only to the Ixia-c software ports.
62 |
63 | | Test Port Speed | DP Cost |
64 | |------------------------|-----------------|
65 | | 1GE | 1 |
66 | | 10GE | 10 |
67 | | 25GE | 25 |
68 | | 40GE | 40 |
69 | | 50GE | 50 |
70 | | 100GE | 100 |
71 | | 200GE | 200 |
72 | | 400GE | 400 |
73 |
74 | ## Sample license consumption scenarios
75 |
76 | ### Test configuration
77 |
78 | Number of `keng-controller` instances: `1`
79 |
80 | * Number of ports: `4`
81 | * Port type: `Ixia-c software`
82 | * Port Speed: `100GE`
83 | * Protocol scale: `100 BGP sessions/port`
84 |
85 | ### Scenario 1: Limited control plane licenses
86 |
87 | ```
88 | KENG-SEAT: 1 = (1 keng-controller instance and ixia-c SW port)
89 | KENG-DPLU: 400 = (100G speed * 4 ports)
90 | KENG-CPLU: 400 = (100 BGP sessions/port * 4 ports)
91 | ```
92 |
93 | ### Scenario 2: Unlimited control plane licenses
94 |
95 | ```
96 | KENG-SEAT: 1 = (1 keng-controller instance and ixia-c SW port)
97 | KENG-DPLU: 400 = (100G speed * 4 ports)
98 | KENG-CPLU: 50 = (CP cost = 400 (100 BGP sessions/port * 4 ports) which is greater than 50 and unlimited cp capability is present)
99 | KENG-UNLIMITED-CP: 1
100 | ```
101 |
102 | ## Q&A
103 |
104 |
105 | When the licenses are checked-out / checked-in?
106 |
107 | The license check-out/check-in mechanism in the keng-controller works as follows:
108 |
109 | 1. Calculate the Test Cost. For example, Test Cost = N.
110 | 2. Based on the calculation performed in step (1), check-out the licenses at the time of the OTG SetConfig API call.
111 | 3. Execute the test if license check-out is successful.
112 | 4. For the next configuration, calculate Test Cost, For example, Test Cost = M.
113 |
114 | ```
115 | if M == N:
116 | - keng-controller will not have any communication with license servers
117 | else if M > N:
118 | - keng-controller will not check-in licenses
119 | - it will attempt to check-out required additional licenses
120 | else if M < N:
121 | - keng-controller will check-in surplus of the licenses
122 | ```
123 |
124 |
125 |
126 | Does licensing have impact on API response time?
127 |
128 | On the timing aspect, the entire license check-out/check-in mechanism works concurrently with the control plane and the data plane configurations in the ports during the OTG SetConfig operation. Therefore, potentially there is a minimal impact in the OTG SetConfig API response time. If the license server is on a remote site from the controller, the OTG SetConfig API response time might get impacted due to the network latency.
129 |
130 |
131 |
132 | What happens if license servers are not available?
133 |
134 | The keng-controller can work with up to 4 license servers. The controller tries to connect to all the license servers during the startup. If none of them is available, the controller capabilities are reduced to the Community Edition. After that, a background routine is initiated to make recurrent attempts to connect the configured license servers in 30 second intervals.
135 |
136 | Once the controller is able to establish a connection with any of the license servers, for any new configuration beyond capabilities of the Community Edition, the keng-controller will try to check-out a license from the license server with which the connection is established.
137 |
138 |
139 |
140 | What is the message generated when the license server is not available?
141 |
142 | When a configured license server is not reachable, the log message with error code 13 is generated by the keng-controller:
143 |
144 | "level":"warn","ctx":"impl/licensing","Not all license server could be reached":"code: 13 ...error details... "
145 |
146 |
147 |
148 | What is the error generated when a license cannot be checked out?
149 |
150 | There are two possible scenarios when the license cannot checkout.
151 |
152 | Scenario 1: Any of the license servers does not have the adequate license features that are required for the test configuration. It will throw an error with the error code 13 and the following error message:
153 |
154 | Current configuration requires following additional license feature(s): {...details...} which is not available in configured license server(s): {...details...} Available license feature(s) in license-server(s) are {...details...}.
155 |
156 |
157 | Scenario 2: Previously active license server is no longer available/reachable. It will throw an error with the error code 13 and the following error message:
158 |
159 | issue consuming license from server ...address...: rpc error: code = DeadlineExceeded desc = context deadline exceeded
160 |
161 |
162 |
163 |
164 | After how many retries things are considered dead and how long does it take?
165 |
166 | The controller will keep probing the list of license servers that are supplied at the time in the background routine in every 30 seconds during the controller lifetime.
167 |
168 |
169 |
170 | What happens if the controller can't check-out the license for a specific server?
171 |
172 | The keng-controller will attempt to check out licenses from the next available license server in the configured list.
173 |
174 |
175 |
176 | How long does a license remain checked-out?
177 |
178 | For the duration of the current test configuration, the license will remain checked-out. Once the new test configuration is applied that doesn't require the license, the license will be checked-in.
179 |
180 |
181 |
182 | How to check-in all the licenses?
183 |
184 | To check-in all the licenses, apply an empty configuration. Alternatively, gracefully stop the keng-controller container.
185 |
186 |
187 |
188 | What happens of the controller can't check-in the license back?
189 |
190 | There is a keep-alive mechanism between the controller and the license server. If the controller crashes, is forcefully stopped, or lost the connection to the license server, the licenses will be automatically checked-in after 5 minutes of keep-alive inactivity.
191 |
192 |
--------------------------------------------------------------------------------