1 | |
2 |
|
3 | function wapiUrl(auth: RT.InfobloxNios, path: string) { |
4 | const host = auth.host |
5 | .trim() |
6 | .replace(/^https?:\/\//, "") |
7 | .replace(/\/+$/, "") |
8 | const version = auth.wapi_version.trim().replace(/^v/, "") |
9 | return new URL(`https://${host}/wapi/v${version}/${path}`) |
10 | } |
11 |
|
12 | function basicAuth(auth: RT.InfobloxNios) { |
13 | const bytes = new TextEncoder().encode(`${auth.username}:${auth.password}`) |
14 | return `Basic ${btoa(String.fromCharCode(...bytes))}` |
15 | } |
16 |
|
17 | async function wapiError(response: Response) { |
18 | const text = await response.text() |
19 | if (response.status === 401) { |
20 | return new Error( |
21 | "401 Unauthorized: wrong username or password, or the user's admin group has no API access" |
22 | ) |
23 | } |
24 | try { |
25 | |
26 | const { Error: error, code, text: detail } = JSON.parse(text) |
27 | if (error) { |
28 | const message = |
29 | detail && !error.includes(detail) ? `${error}: ${detail}` : error |
30 | return new Error( |
31 | `${response.status} ${message}${code ? ` (${code})` : ""}` |
32 | ) |
33 | } |
34 | } catch {} |
35 | return new Error(`${response.status} ${text}`) |
36 | } |
37 |
|
38 | |
39 | * Search fixed addresses |
40 | * Search static DHCP reservations (fixedaddress) by MAC address (any of aa:bb:cc:dd:ee:ff, AA-BB-CC-DD-EE-FF or aabb.ccdd.eeff), IPv4 address, network CIDR, network view or extensible attribute values (extattr_filters {"Site": "Paris"}), one page at a time. Pass the returned next_page_id as page_id to get the next page; it is null on the last page. |
41 | */ |
42 | export async function main( |
43 | auth: RT.InfobloxNios, |
44 | mac: string | undefined, |
45 | ipv4addr: string | undefined, |
46 | network: string | undefined, |
47 | network_view: string | undefined, |
48 | extattr_filters: { [key: string]: string } | undefined, |
49 | max_results: number | undefined, |
50 | page_id: string | undefined, |
51 | return_fields: string[] | undefined |
52 | ) { |
53 | const url = wapiUrl(auth, "fixedaddress") |
54 | if (mac) { |
55 | |
56 | const hex = mac |
57 | .trim() |
58 | .toLowerCase() |
59 | .replace(/[^0-9a-f]/g, "") |
60 | url.searchParams.append( |
61 | "mac", |
62 | hex.length === 12 ? hex.match(/../g)!.join(":") : mac.trim().toLowerCase() |
63 | ) |
64 | } |
65 | if (ipv4addr) url.searchParams.append("ipv4addr", ipv4addr) |
66 | if (network) url.searchParams.append("network", network) |
67 | if (network_view) url.searchParams.append("network_view", network_view) |
68 | for (const [name, value] of Object.entries(extattr_filters ?? {})) { |
69 | if (value != null && value !== "") |
70 | url.searchParams.append(`*${name}`, String(value)) |
71 | } |
72 | const fields = [ |
73 | "mac", |
74 | "name", |
75 | "network", |
76 | "comment", |
77 | "extattrs", |
78 | ...(return_fields ?? []), |
79 | ] |
80 | url.searchParams.append("_return_fields+", fields.join(",")) |
81 | url.searchParams.append("_paging", "1") |
82 | url.searchParams.append("_return_as_object", "1") |
83 | url.searchParams.append("_max_results", String(max_results || 100)) |
84 | if (page_id) url.searchParams.append("_page_id", page_id) |
85 |
|
86 | const response = await fetch(url, { |
87 | headers: { |
88 | Authorization: basicAuth(auth), |
89 | Accept: "application/json", |
90 | }, |
91 | }) |
92 |
|
93 | if (!response.ok) { |
94 | throw await wapiError(response) |
95 | } |
96 |
|
97 | const data = await response.json() |
98 | return { result: data.result, next_page_id: data.next_page_id ?? null } |
99 | } |
100 |
|