1 | |
2 |
|
3 | |
4 | async function authHeaders( |
5 | auth: RT.IxiaIxnetwork, |
6 | base: string |
7 | ): Promise<Record<string, string>> { |
8 | if (auth.api_key) return { "X-Api-Key": auth.api_key } |
9 | if (!auth.username) return {} |
10 | const response = await fetch(`${base}/api/v1/auth/session`, { |
11 | method: "POST", |
12 | headers: { "Content-Type": "application/json", Accept: "application/json" }, |
13 | body: JSON.stringify({ username: auth.username, password: auth.password }), |
14 | }) |
15 | if (!response.ok) { |
16 | throw new Error(`${response.status} ${await response.text()}`) |
17 | } |
18 | const { apiKey } = (await response.json()) as { apiKey: string } |
19 | return { "X-Api-Key": apiKey } |
20 | } |
21 |
|
22 | type Operation = { |
23 | id?: string |
24 | state?: string |
25 | progress?: number |
26 | message?: string |
27 | result?: unknown |
28 | url?: string |
29 | } |
30 |
|
31 | |
32 | async function runTrafficOperation( |
33 | base: string, |
34 | headers: Record<string, string>, |
35 | session_id: number, |
36 | operation: string, |
37 | wait: boolean, |
38 | deadline: number |
39 | ): Promise<Operation> { |
40 | const traffic = `/api/v1/sessions/${session_id}/ixnetwork/traffic` |
41 | const response = await fetch(`${base}${traffic}/operations/${operation}`, { |
42 | method: "POST", |
43 | headers: { |
44 | ...headers, |
45 | "Content-Type": "application/json", |
46 | Accept: "application/json", |
47 | }, |
48 | body: JSON.stringify({ arg1: traffic }), |
49 | }) |
50 | if (!response.ok) { |
51 | throw new Error(`${response.status} ${await response.text()}`) |
52 | } |
53 | let status = (await response.json()) as Operation |
54 | const statusUrl = status.url |
55 | const pollUrl = statusUrl?.startsWith("http") |
56 | ? statusUrl |
57 | : `${base}/${statusUrl?.replace(/^\/+/, "")}` |
58 |
|
59 | while (wait && status.state === "IN_PROGRESS" && statusUrl) { |
60 | if (Date.now() > deadline) { |
61 | throw new Error( |
62 | `Traffic ${operation} still in progress at timeout: ${JSON.stringify(status)}` |
63 | ) |
64 | } |
65 | await new Promise((r) => setTimeout(r, 1000)) |
66 | const poll = await fetch(pollUrl, { |
67 | headers: { ...headers, Accept: "application/json" }, |
68 | }) |
69 | if (!poll.ok) { |
70 | throw new Error(`${poll.status} ${await poll.text()}`) |
71 | } |
72 | status = (await poll.json()) as Operation |
73 | } |
74 |
|
75 | if (status.state === "ERROR" || status.state === "EXCEPTION") { |
76 | throw new Error(`Traffic ${operation} failed: ${JSON.stringify(status)}`) |
77 | } |
78 | return status |
79 | } |
80 |
|
81 | |
82 | * Start traffic |
83 | * Start the traffic of an IxNetwork session, applying the traffic configuration first unless apply_traffic is off (turn it off while a packet capture is running, since applying stops the capture). |
84 | */ |
85 | export async function main( |
86 | auth: RT.IxiaIxnetwork, |
87 | session_id: number, |
88 | apply_traffic: boolean = true, |
89 | wait_for_completion: boolean = true, |
90 | timeout_seconds: number = 120 |
91 | ) { |
92 | const base = auth.base_url.replace(/\/+$/, "") |
93 | const headers = await authHeaders(auth, base) |
94 | const deadline = Date.now() + (timeout_seconds || 120) * 1000 |
95 |
|
96 | |
97 | const apply = apply_traffic |
98 | ? await runTrafficOperation( |
99 | base, |
100 | headers, |
101 | session_id, |
102 | "apply", |
103 | true, |
104 | deadline |
105 | ) |
106 | : undefined |
107 | const start = await runTrafficOperation( |
108 | base, |
109 | headers, |
110 | session_id, |
111 | "start", |
112 | wait_for_completion !== false, |
113 | deadline |
114 | ) |
115 |
|
116 | return { apply, start } |
117 | } |
118 |
|