//native
// X-Api-Key header: the resource's api_key, or one obtained from username/password
async function authHeaders(
auth: RT.IxiaIxnetwork,
base: string
): Promise<Record<string, string>> {
if (auth.api_key) return { "X-Api-Key": auth.api_key }
if (!auth.username) return {}
const response = await fetch(`${base}/api/v1/auth/session`, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({ username: auth.username, password: auth.password }),
})
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
const { apiKey } = (await response.json()) as { apiKey: string }
return { "X-Api-Key": apiKey }
}
type Page = {
isReady?: boolean
currentPage?: number
pageSize?: number
totalPages?: number
totalRows?: number
columnCaptions?: string[]
pageValues?: (string | number | null)[][][]
}
/**
* Get traffic statistics
* Read one page of a statistics view: "Traffic Item Statistics" (default) or "Flow Statistics". Returns the page's rows keyed by column caption, plus current_page and total_pages to fetch the next page.
*/
export async function main(
auth: RT.IxiaIxnetwork,
session_id: number,
view_caption: string = "Traffic Item Statistics",
page: number = 1,
page_size: number | undefined
) {
const base = auth.base_url.replace(/\/+$/, "")
const headers = {
...(await authHeaders(auth, base)),
Accept: "application/json",
}
const caption = view_caption || "Traffic Item Statistics"
const viewsUrl = `${base}/api/v1/sessions/${session_id}/ixnetwork/statistics/view`
const viewsResponse = await fetch(viewsUrl, { headers })
if (!viewsResponse.ok) {
throw new Error(`${viewsResponse.status} ${await viewsResponse.text()}`)
}
const views = (await viewsResponse.json()) as {
id: number
caption?: string
}[]
// Keysight's clients read each view's caption with a GET on the view; skip it when the list already has it
let view: { id: number; caption?: string } | undefined
const captions: string[] = []
for (const item of views) {
let current = item
if (current.caption == null) {
const viewResponse = await fetch(`${viewsUrl}/${item.id}`, { headers })
if (!viewResponse.ok) {
throw new Error(`${viewResponse.status} ${await viewResponse.text()}`)
}
current = (await viewResponse.json()) as { id: number; caption?: string }
}
captions.push(current.caption ?? "")
if (current.caption?.toLowerCase() === caption.toLowerCase()) {
view = current
break
}
}
if (!view) {
throw new Error(
`No statistics view "${caption}". Available views: ${captions.join(", ")}`
)
}
// /data replaces /page, deprecated since IxNetwork 8.50
const dataUrl = `${viewsUrl}/${view.id}/data`
// the view's current page is server-side state: select the page (and size) before reading it
const patch = await fetch(dataUrl, {
method: "PATCH",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
currentPage: page || 1,
pageSize: page_size || undefined,
}),
})
if (!patch.ok) {
throw new Error(`${patch.status} ${await patch.text()}`)
}
const response = await fetch(dataUrl, { headers })
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
const data = (await response.json()) as Page
const columns = data.columnCaptions ?? []
// each pageValues entry groups an ingress row with its egress-tracking rows; the first is the row itself
const rows = (data.pageValues ?? []).map((group) =>
Object.fromEntries(columns.map((c, i) => [c, group[0]?.[i]]))
)
return {
view_id: view.id,
caption: view.caption,
is_ready: data.isReady,
current_page: data.currentPage,
page_size: data.pageSize,
total_pages: data.totalPages,
total_rows: data.totalRows,
column_captions: columns,
rows,
page_values: data.pageValues,
}
}
Submitted by hugo989 6 hours ago