1 | |
2 |
|
3 | async function connect(auth: RT.F5Bigip): Promise<{ |
4 | base: string |
5 | headers: { [key: string]: string } |
6 | release: () => Promise<void> |
7 | }> { |
8 | const base = auth.base_url.replace(/\/+$/, "") |
9 | if (auth.auth_method === "basic") { |
10 | return { |
11 | base, |
12 | headers: { |
13 | Authorization: `Basic ${btoa(`${auth.username}:${auth.password}`)}`, |
14 | }, |
15 | release: async () => {}, |
16 | } |
17 | } |
18 | const login = await fetch(`${base}/mgmt/shared/authn/login`, { |
19 | method: "POST", |
20 | headers: { "Content-Type": "application/json", Accept: "application/json" }, |
21 | body: JSON.stringify({ |
22 | username: auth.username, |
23 | password: auth.password, |
24 | loginProviderName: auth.login_provider_name || "tmos", |
25 | }), |
26 | }) |
27 | if (!login.ok) { |
28 | throw new Error(`${login.status} ${await login.text()}`) |
29 | } |
30 | const token: string = (await login.json()).token.token |
31 | return { |
32 | base, |
33 | headers: { "X-F5-Auth-Token": token }, |
34 | release: async () => { |
35 | await fetch( |
36 | `${base}/mgmt/shared/authz/tokens/${encodeURIComponent(token)}`, |
37 | { |
38 | method: "DELETE", |
39 | headers: { "X-F5-Auth-Token": token }, |
40 | } |
41 | ) |
42 | }, |
43 | } |
44 | } |
45 |
|
46 | |
47 | * List virtual servers |
48 | * List LTM virtual servers, one page at a time ($top / $skip). The response keeps BIG-IP's items, nextLink and totalItems, plus next_skip to pass as skip for the next page (null on the last page). |
49 | */ |
50 | export async function main( |
51 | auth: RT.F5Bigip, |
52 | partition: string | undefined, |
53 | top: number | undefined, |
54 | skip: number | undefined |
55 | ) { |
56 | const f5 = await connect(auth) |
57 | try { |
58 | const url = new URL(`${f5.base}/mgmt/tm/ltm/virtual`) |
59 | const pageSize = top || 100 |
60 | const offset = skip || 0 |
61 | url.searchParams.append("$top", String(pageSize)) |
62 | url.searchParams.append("$skip", String(offset)) |
63 | if (partition != null && partition !== "") { |
64 | url.searchParams.append("$filter", `partition eq ${partition}`) |
65 | } |
66 |
|
67 | const response = await fetch(url, { |
68 | headers: { ...f5.headers, Accept: "application/json" }, |
69 | }) |
70 |
|
71 | if (!response.ok) { |
72 | throw new Error(`${response.status} ${await response.text()}`) |
73 | } |
74 |
|
75 | const page = await response.json() |
76 | const count = (page.items ?? []).length |
77 | |
78 | |
79 | const more = |
80 | count > 0 && |
81 | (page.totalItems != null |
82 | ? offset + count < page.totalItems |
83 | : count === pageSize) |
84 | return { ...page, next_skip: more ? offset + count : null } |
85 | } finally { |
86 | await f5.release() |
87 | } |
88 | } |
89 |
|