cisco-perfmon 1.5.3 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +37 -0
- package/main.js +152 -72
- package/main.mjs +5 -0
- package/package.json +15 -7
- package/types/index.d.ts +166 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Setup Node.js
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: '22'
|
|
23
|
+
registry-url: 'https://registry.npmjs.org'
|
|
24
|
+
|
|
25
|
+
- name: Update npm
|
|
26
|
+
run: npm install -g npm@latest
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: npm ci
|
|
30
|
+
|
|
31
|
+
- name: Publish to npm
|
|
32
|
+
run: npm publish --access public
|
|
33
|
+
|
|
34
|
+
- name: Create GitHub Release
|
|
35
|
+
uses: softprops/action-gh-release@v2
|
|
36
|
+
with:
|
|
37
|
+
generate_release_notes: true
|
package/main.js
CHANGED
|
@@ -1,86 +1,104 @@
|
|
|
1
1
|
const fetch = require("fetch-retry")(global.fetch);
|
|
2
|
-
const util = require("util");
|
|
3
2
|
const parseString = require("xml2js").parseString;
|
|
4
3
|
const stripPrefix = require("xml2js").processors.stripPrefix;
|
|
5
4
|
const http = require("http");
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
const escapeXml = (str) => {
|
|
7
|
+
if (typeof str !== "string") return str;
|
|
8
|
+
return str
|
|
9
|
+
.replace(/&/g, "&")
|
|
10
|
+
.replace(/</g, "<")
|
|
11
|
+
.replace(/>/g, ">")
|
|
12
|
+
.replace(/"/g, """)
|
|
13
|
+
.replace(/'/g, "'");
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const buildCounterStr = (c) => {
|
|
17
|
+
const host = escapeXml(c.host);
|
|
18
|
+
const object = escapeXml(c.object);
|
|
19
|
+
const instance = c.instance ? escapeXml(c.instance) : "";
|
|
20
|
+
const counter = c.counter ? escapeXml(c.counter) : "";
|
|
21
|
+
const objectPart = instance ? `${object}(${instance})` : object;
|
|
22
|
+
return `<soap:Counter><soap:Name>\\\\${host}\\${objectPart}\\${counter}</soap:Name></soap:Counter>`;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const XML_ADD_COUNTER_ENVELOPE = (sessionHandle, counters) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
8
26
|
<soapenv:Header/>
|
|
9
27
|
<soapenv:Body>
|
|
10
28
|
<soap:perfmonAddCounter>
|
|
11
|
-
<soap:SessionHandle
|
|
12
|
-
<soap:ArrayOfCounter
|
|
29
|
+
<soap:SessionHandle>${sessionHandle}</soap:SessionHandle>
|
|
30
|
+
<soap:ArrayOfCounter>${counters}</soap:ArrayOfCounter>
|
|
13
31
|
</soap:perfmonAddCounter>
|
|
14
32
|
</soapenv:Body>
|
|
15
33
|
</soapenv:Envelope>`;
|
|
16
34
|
|
|
17
|
-
|
|
35
|
+
const XML_CLOSE_SESSION_ENVELOPE = (sessionHandle) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
18
36
|
<soapenv:Header/>
|
|
19
37
|
<soapenv:Body>
|
|
20
38
|
<soap:perfmonCloseSession>
|
|
21
|
-
<soap:SessionHandle
|
|
39
|
+
<soap:SessionHandle>${sessionHandle}</soap:SessionHandle>
|
|
22
40
|
</soap:perfmonCloseSession>
|
|
23
41
|
</soapenv:Body>
|
|
24
42
|
</soapenv:Envelope>`;
|
|
25
43
|
|
|
26
|
-
|
|
44
|
+
const XML_COLLECT_COUNTER_ENVELOPE = (host, object) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
27
45
|
<soapenv:Header/>
|
|
28
46
|
<soapenv:Body>
|
|
29
47
|
<soap:perfmonCollectCounterData>
|
|
30
|
-
<soap:Host
|
|
31
|
-
<soap:Object
|
|
48
|
+
<soap:Host>${host}</soap:Host>
|
|
49
|
+
<soap:Object>${object}</soap:Object>
|
|
32
50
|
</soap:perfmonCollectCounterData>
|
|
33
51
|
</soapenv:Body>
|
|
34
52
|
</soapenv:Envelope>`;
|
|
35
53
|
|
|
36
|
-
|
|
54
|
+
const XML_COLLECT_SESSION_ENVELOPE = (sessionHandle) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
37
55
|
<soapenv:Header/>
|
|
38
56
|
<soapenv:Body>
|
|
39
57
|
<soap:perfmonCollectSessionData>
|
|
40
|
-
<soap:SessionHandle
|
|
58
|
+
<soap:SessionHandle>${sessionHandle}</soap:SessionHandle>
|
|
41
59
|
</soap:perfmonCollectSessionData>
|
|
42
60
|
</soapenv:Body>
|
|
43
61
|
</soapenv:Envelope>`;
|
|
44
62
|
|
|
45
|
-
|
|
63
|
+
const XML_LIST_COUNTER_ENVELOPE = (host) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
46
64
|
<soapenv:Header/>
|
|
47
65
|
<soapenv:Body>
|
|
48
66
|
<soap:perfmonListCounter>
|
|
49
|
-
<soap:Host
|
|
67
|
+
<soap:Host>${host}</soap:Host>
|
|
50
68
|
</soap:perfmonListCounter>
|
|
51
69
|
</soapenv:Body>
|
|
52
70
|
</soapenv:Envelope>`;
|
|
53
71
|
|
|
54
|
-
|
|
72
|
+
const XML_LIST_INSTANCE_ENVELOPE = (host, object) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
55
73
|
<soapenv:Header/>
|
|
56
74
|
<soapenv:Body>
|
|
57
75
|
<soap:perfmonListInstance>
|
|
58
|
-
<soap:Host
|
|
59
|
-
<soap:Object
|
|
76
|
+
<soap:Host>${host}</soap:Host>
|
|
77
|
+
<soap:Object>${object}</soap:Object>
|
|
60
78
|
</soap:perfmonListInstance>
|
|
61
79
|
</soapenv:Body>
|
|
62
80
|
</soapenv:Envelope>`;
|
|
63
81
|
|
|
64
|
-
|
|
82
|
+
const XML_OPEN_SESSION_ENVELOPE = () => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
65
83
|
<soapenv:Header/>
|
|
66
84
|
<soapenv:Body>
|
|
67
85
|
<soap:perfmonOpenSession/>
|
|
68
86
|
</soapenv:Body>
|
|
69
87
|
</soapenv:Envelope>`;
|
|
70
88
|
|
|
71
|
-
|
|
89
|
+
const XML_QUERY_COUNTER_ENVELOPE = (counter) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
72
90
|
<soapenv:Header/>
|
|
73
91
|
<soapenv:Body>
|
|
74
|
-
<soap:perfmonQueryCounterDescription
|
|
92
|
+
<soap:perfmonQueryCounterDescription>${counter}</soap:perfmonQueryCounterDescription>
|
|
75
93
|
</soapenv:Body>
|
|
76
94
|
</soapenv:Envelope>`;
|
|
77
95
|
|
|
78
|
-
|
|
96
|
+
const XML_REMOVE_COUNTER_ENVELOPE = (sessionHandle, counters) => `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
|
|
79
97
|
<soapenv:Header/>
|
|
80
98
|
<soapenv:Body>
|
|
81
99
|
<soap:perfmonRemoveCounter>
|
|
82
|
-
<soap:SessionHandle
|
|
83
|
-
<soap:ArrayOfCounter
|
|
100
|
+
<soap:SessionHandle>${sessionHandle}</soap:SessionHandle>
|
|
101
|
+
<soap:ArrayOfCounter>${counters}</soap:ArrayOfCounter>
|
|
84
102
|
</soap:perfmonRemoveCounter>
|
|
85
103
|
</soapenv:Body>
|
|
86
104
|
</soapenv:Envelope>`;
|
|
@@ -94,23 +112,44 @@ var XML_REMOVE_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schem
|
|
|
94
112
|
* @param {string} host - The host to collect data from. This is usually the IP address/FQDN of the CUCM publisher.
|
|
95
113
|
* @param {string} username - The username to authenticate with. This is usually an AXL user. Can leave this blank if using JESSIONSSO cookie.
|
|
96
114
|
* @param {string} password - The password to authenticate with. This is usually an AXL user. Can leave this blank if using JESSIONSSO cookie.
|
|
97
|
-
* @param {object} options -
|
|
115
|
+
* @param {object} options - Options object. Supports `retries` (default 3), `retryDelay` ms (default 5000), and any additional headers (e.g. cookies for SSO).
|
|
98
116
|
* @returns {object} returns constructor object.
|
|
99
117
|
*/
|
|
100
118
|
class perfMonService {
|
|
101
119
|
constructor(host, username, password, options = {}, retry = true) {
|
|
120
|
+
const RATE_LIMIT_DELAYS = [30000, 60000, 120000]; // 30s, 60s, 120s exponential backoff
|
|
121
|
+
const maxRetries = options.retries ?? (process.env.PM_RETRY ? parseInt(process.env.PM_RETRY) : 3);
|
|
122
|
+
const retryDelay = options.retryDelay ?? (process.env.PM_RETRY_DELAY ? parseInt(process.env.PM_RETRY_DELAY) : 5000);
|
|
123
|
+
|
|
102
124
|
this._OPTIONS = {
|
|
103
125
|
retryOn: async function (attempt, error, response) {
|
|
104
126
|
if (!retry) {
|
|
105
127
|
return false;
|
|
106
128
|
}
|
|
107
|
-
if (attempt >
|
|
129
|
+
if (attempt > maxRetries) {
|
|
108
130
|
return false;
|
|
109
131
|
}
|
|
132
|
+
|
|
133
|
+
// Check for SOAP-level rate limiting (CUCM 80 req/min limit)
|
|
134
|
+
if (response && response.status === 500) {
|
|
135
|
+
try {
|
|
136
|
+
const clonedResponse = response.clone();
|
|
137
|
+
const body = await clonedResponse.text();
|
|
138
|
+
if (body.includes("Exceeded allowed rate for Perfmon")) {
|
|
139
|
+
const backoff = RATE_LIMIT_DELAYS[Math.min(attempt, RATE_LIMIT_DELAYS.length - 1)];
|
|
140
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
141
|
+
await delay(backoff);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
145
|
+
// If we can't read the body, fall through to normal retry
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
110
149
|
// retry on any network error, or 4xx or 5xx status codes
|
|
111
150
|
if (error !== null || response.status >= 400) {
|
|
112
151
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
113
|
-
await delay(
|
|
152
|
+
await delay(retryDelay);
|
|
114
153
|
return true;
|
|
115
154
|
}
|
|
116
155
|
},
|
|
@@ -128,6 +167,28 @@ class perfMonService {
|
|
|
128
167
|
}
|
|
129
168
|
|
|
130
169
|
this._HOST = host;
|
|
170
|
+
this._COOKIE = "";
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get the current stored cookie
|
|
174
|
+
*
|
|
175
|
+
* @getCookie
|
|
176
|
+
* @memberof perfMonService
|
|
177
|
+
* @returns {string} returns the stored cookie string.
|
|
178
|
+
*/
|
|
179
|
+
getCookie() {
|
|
180
|
+
return this._COOKIE;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Set a cookie to be used for subsequent requests
|
|
184
|
+
*
|
|
185
|
+
* @setCookie
|
|
186
|
+
* @memberof perfMonService
|
|
187
|
+
* @param {string} cookie - The cookie string to set.
|
|
188
|
+
*/
|
|
189
|
+
setCookie(cookie) {
|
|
190
|
+
this._COOKIE = cookie;
|
|
191
|
+
this._OPTIONS.headers.Cookie = cookie;
|
|
131
192
|
}
|
|
132
193
|
/**
|
|
133
194
|
* Post Fetch using Cisco PerfMon API
|
|
@@ -145,9 +206,9 @@ class perfMonService {
|
|
|
145
206
|
*/
|
|
146
207
|
async collectCounterData(host, object) {
|
|
147
208
|
try {
|
|
148
|
-
let options = this._OPTIONS;
|
|
209
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
149
210
|
let server = this._HOST;
|
|
150
|
-
let XML =
|
|
211
|
+
let XML = XML_COLLECT_COUNTER_ENVELOPE(escapeXml(host), escapeXml(object));
|
|
151
212
|
let soapBody = Buffer.from(XML);
|
|
152
213
|
options.body = soapBody;
|
|
153
214
|
options.SOAPAction = `perfmonCollectCounterData`;
|
|
@@ -161,6 +222,9 @@ class perfMonService {
|
|
|
161
222
|
};
|
|
162
223
|
|
|
163
224
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
225
|
+
if (promiseResults.cookie) {
|
|
226
|
+
this.setCookie(promiseResults.cookie);
|
|
227
|
+
}
|
|
164
228
|
|
|
165
229
|
let output = await parseXml(await response.text());
|
|
166
230
|
// Remove unnecessary keys
|
|
@@ -198,9 +262,9 @@ class perfMonService {
|
|
|
198
262
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
199
263
|
*/
|
|
200
264
|
async collectSessionData(SessionHandle) {
|
|
201
|
-
let options = this._OPTIONS;
|
|
265
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
202
266
|
let server = this._HOST;
|
|
203
|
-
let XML =
|
|
267
|
+
let XML = XML_COLLECT_SESSION_ENVELOPE(SessionHandle);
|
|
204
268
|
let soapBody = Buffer.from(XML);
|
|
205
269
|
options.body = soapBody;
|
|
206
270
|
options.SOAPAction = `perfmonCollectSessionData`;
|
|
@@ -213,6 +277,9 @@ class perfMonService {
|
|
|
213
277
|
};
|
|
214
278
|
|
|
215
279
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
280
|
+
if (promiseResults.cookie) {
|
|
281
|
+
this.setCookie(promiseResults.cookie);
|
|
282
|
+
}
|
|
216
283
|
|
|
217
284
|
let output = await parseXml(await response.text());
|
|
218
285
|
// Remove unnecessary keys
|
|
@@ -246,9 +313,9 @@ class perfMonService {
|
|
|
246
313
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
247
314
|
*/
|
|
248
315
|
async listCounter(host, filtered = []) {
|
|
249
|
-
let options = this._OPTIONS;
|
|
316
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
250
317
|
let server = this._HOST;
|
|
251
|
-
let XML =
|
|
318
|
+
let XML = XML_LIST_COUNTER_ENVELOPE(escapeXml(host));
|
|
252
319
|
let soapBody = Buffer.from(XML);
|
|
253
320
|
options.body = soapBody;
|
|
254
321
|
options.SOAPAction = `perfmonListCounter`;
|
|
@@ -261,6 +328,9 @@ class perfMonService {
|
|
|
261
328
|
};
|
|
262
329
|
|
|
263
330
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
331
|
+
if (promiseResults.cookie) {
|
|
332
|
+
this.setCookie(promiseResults.cookie);
|
|
333
|
+
}
|
|
264
334
|
|
|
265
335
|
let output = await parseXml(await response.text());
|
|
266
336
|
// Remove unnecessary keys
|
|
@@ -299,9 +369,9 @@ class perfMonService {
|
|
|
299
369
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
300
370
|
*/
|
|
301
371
|
async listInstance(host, object) {
|
|
302
|
-
let options = this._OPTIONS;
|
|
372
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
303
373
|
let server = this._HOST;
|
|
304
|
-
let XML =
|
|
374
|
+
let XML = XML_LIST_INSTANCE_ENVELOPE(escapeXml(host), escapeXml(object));
|
|
305
375
|
let soapBody = Buffer.from(XML);
|
|
306
376
|
options.body = soapBody;
|
|
307
377
|
options.SOAPAction = `perfmonListInstance`;
|
|
@@ -315,6 +385,9 @@ class perfMonService {
|
|
|
315
385
|
};
|
|
316
386
|
|
|
317
387
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
388
|
+
if (promiseResults.cookie) {
|
|
389
|
+
this.setCookie(promiseResults.cookie);
|
|
390
|
+
}
|
|
318
391
|
|
|
319
392
|
let output = await parseXml(await response.text());
|
|
320
393
|
// Remove unnecessary keys
|
|
@@ -331,11 +404,7 @@ class perfMonService {
|
|
|
331
404
|
|
|
332
405
|
// If the results are not an array, we make it an array.
|
|
333
406
|
if (!Array.isArray(promiseResults.results)) {
|
|
334
|
-
|
|
335
|
-
promiseResults = {
|
|
336
|
-
results: [],
|
|
337
|
-
};
|
|
338
|
-
promiseResults.results.push(temp);
|
|
407
|
+
promiseResults.results = [promiseResults.results];
|
|
339
408
|
}
|
|
340
409
|
return promiseResults;
|
|
341
410
|
} else {
|
|
@@ -356,9 +425,9 @@ class perfMonService {
|
|
|
356
425
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
357
426
|
*/
|
|
358
427
|
async openSession() {
|
|
359
|
-
let options = this._OPTIONS;
|
|
428
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
360
429
|
let server = this._HOST;
|
|
361
|
-
let XML =
|
|
430
|
+
let XML = XML_OPEN_SESSION_ENVELOPE();
|
|
362
431
|
let soapBody = Buffer.from(XML);
|
|
363
432
|
options.body = soapBody;
|
|
364
433
|
options.SOAPAction = `perfmonOpenSession`;
|
|
@@ -371,6 +440,9 @@ class perfMonService {
|
|
|
371
440
|
};
|
|
372
441
|
|
|
373
442
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
443
|
+
if (promiseResults.cookie) {
|
|
444
|
+
this.setCookie(promiseResults.cookie);
|
|
445
|
+
}
|
|
374
446
|
|
|
375
447
|
let output = await parseXml(await response.text());
|
|
376
448
|
// Remove unnecessary keys
|
|
@@ -404,9 +476,9 @@ class perfMonService {
|
|
|
404
476
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
405
477
|
*/
|
|
406
478
|
async closeSession(sessionHandle) {
|
|
407
|
-
let options = this._OPTIONS;
|
|
479
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
408
480
|
let server = this._HOST;
|
|
409
|
-
let XML =
|
|
481
|
+
let XML = XML_CLOSE_SESSION_ENVELOPE(sessionHandle);
|
|
410
482
|
let soapBody = Buffer.from(XML);
|
|
411
483
|
options.body = soapBody;
|
|
412
484
|
options.SOAPAction = `perfmonCloseSession`;
|
|
@@ -419,6 +491,9 @@ class perfMonService {
|
|
|
419
491
|
};
|
|
420
492
|
|
|
421
493
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
494
|
+
if (promiseResults.cookie) {
|
|
495
|
+
this.setCookie(promiseResults.cookie);
|
|
496
|
+
}
|
|
422
497
|
|
|
423
498
|
let output = await parseXml(await response.text());
|
|
424
499
|
// Remove unnecessary keys
|
|
@@ -451,16 +526,10 @@ class perfMonService {
|
|
|
451
526
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
452
527
|
*/
|
|
453
528
|
async addCounter(sessionHandle, counter) {
|
|
454
|
-
let options = this._OPTIONS;
|
|
529
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
455
530
|
let server = this._HOST;
|
|
456
|
-
let counterStr =
|
|
457
|
-
|
|
458
|
-
if (Array.isArray(counter)) {
|
|
459
|
-
counter.forEach((counter) => (counterStr += "<soap:Counter>" + "<soap:Name>" + "\\\\" + counter.host + "\\" + (counter.instance ? `${counter.object}(${counter.instance})` : counter.object) + "\\" + counter.counter + "</soap:Name>" + "</soap:Counter>"));
|
|
460
|
-
} else {
|
|
461
|
-
counterStr = "<soap:Counter>" + "<soap:Name>" + "\\\\" + counter.host + "\\" + (counter.instance ? `${counter.object}(${counter.instance})` : counter.object) + "\\" + counter.counter + "</soap:Name>" + "</soap:Counter>";
|
|
462
|
-
}
|
|
463
|
-
let XML = util.format(XML_ADD_COUNTER_ENVELOPE, sessionHandle, counterStr);
|
|
531
|
+
let counterStr = Array.isArray(counter) ? counter.map(buildCounterStr).join("") : buildCounterStr(counter);
|
|
532
|
+
let XML = XML_ADD_COUNTER_ENVELOPE(sessionHandle, counterStr);
|
|
464
533
|
let soapBody = Buffer.from(XML);
|
|
465
534
|
options.body = soapBody;
|
|
466
535
|
options.SOAPAction = `perfmonAddCounter`;
|
|
@@ -473,6 +542,9 @@ class perfMonService {
|
|
|
473
542
|
};
|
|
474
543
|
|
|
475
544
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
545
|
+
if (promiseResults.cookie) {
|
|
546
|
+
this.setCookie(promiseResults.cookie);
|
|
547
|
+
}
|
|
476
548
|
|
|
477
549
|
let output = await parseXml(await response.text());
|
|
478
550
|
// Remove unnecessary keys
|
|
@@ -505,16 +577,10 @@ class perfMonService {
|
|
|
505
577
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
506
578
|
*/
|
|
507
579
|
async removeCounter(sessionHandle, counter) {
|
|
508
|
-
let options = this._OPTIONS;
|
|
580
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
509
581
|
let server = this._HOST;
|
|
510
|
-
let counterStr =
|
|
511
|
-
|
|
512
|
-
counter.forEach((counter) => (counterStr += "<soap:Counter>" + "<soap:Name>" + "\\\\" + counter.host + "\\" + (counter.instance ? `${counter.object}(${counter.instance})` : counter.object) + "\\" + counter.counter + "</soap:Name>" + "</soap:Counter>"));
|
|
513
|
-
} else {
|
|
514
|
-
counterStr = "<soap:Counter>" + "<soap:Name>" + "\\\\" + counter.host + "\\" + (counter.instance ? `${counter.object}(${counter.instance})` : counter.object) + "\\" + counter.counter + "</soap:Name>" + "</soap:Counter>";
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
let XML = util.format(XML_REMOVE_COUNTER_ENVELOPE, sessionHandle, counterStr);
|
|
582
|
+
let counterStr = Array.isArray(counter) ? counter.map(buildCounterStr).join("") : buildCounterStr(counter);
|
|
583
|
+
let XML = XML_REMOVE_COUNTER_ENVELOPE(sessionHandle, counterStr);
|
|
518
584
|
let soapBody = Buffer.from(XML);
|
|
519
585
|
options.body = soapBody;
|
|
520
586
|
options.SOAPAction = `perfmonRemoveCounter`;
|
|
@@ -527,6 +593,9 @@ class perfMonService {
|
|
|
527
593
|
};
|
|
528
594
|
|
|
529
595
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
596
|
+
if (promiseResults.cookie) {
|
|
597
|
+
this.setCookie(promiseResults.cookie);
|
|
598
|
+
}
|
|
530
599
|
|
|
531
600
|
let output = await parseXml(await response.text());
|
|
532
601
|
// Remove unnecessary keys
|
|
@@ -558,10 +627,10 @@ class perfMonService {
|
|
|
558
627
|
* @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
|
|
559
628
|
*/
|
|
560
629
|
async queryCounterDescription(object) {
|
|
561
|
-
let options = this._OPTIONS;
|
|
630
|
+
let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
|
|
562
631
|
let server = this._HOST;
|
|
563
|
-
let counterStr =
|
|
564
|
-
let XML =
|
|
632
|
+
let counterStr = buildCounterStr(object);
|
|
633
|
+
let XML = XML_QUERY_COUNTER_ENVELOPE(counterStr);
|
|
565
634
|
let soapBody = Buffer.from(XML);
|
|
566
635
|
options.body = soapBody;
|
|
567
636
|
options.SOAPAction = `perfmonQueryCounterDescription`;
|
|
@@ -575,6 +644,9 @@ class perfMonService {
|
|
|
575
644
|
};
|
|
576
645
|
|
|
577
646
|
promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
|
|
647
|
+
if (promiseResults.cookie) {
|
|
648
|
+
this.setCookie(promiseResults.cookie);
|
|
649
|
+
}
|
|
578
650
|
|
|
579
651
|
let output = await parseXml(await response.text());
|
|
580
652
|
// Remove unnecessary keys
|
|
@@ -626,18 +698,26 @@ const removeKeys = (obj, keys) => {
|
|
|
626
698
|
};
|
|
627
699
|
|
|
628
700
|
const clean = (object) => {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
701
|
+
if (Array.isArray(object)) {
|
|
702
|
+
for (let i = object.length - 1; i >= 0; i--) {
|
|
703
|
+
const v = object[i];
|
|
704
|
+
if (v && typeof v === "object") {
|
|
705
|
+
clean(v);
|
|
706
|
+
}
|
|
707
|
+
if ((v && typeof v === "object" && !Object.keys(v).length) || v === null || v === undefined) {
|
|
708
|
+
object.splice(i, 1);
|
|
709
|
+
}
|
|
632
710
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
711
|
+
} else {
|
|
712
|
+
Object.entries(object).forEach(([k, v]) => {
|
|
713
|
+
if (v && typeof v === "object") {
|
|
714
|
+
clean(v);
|
|
715
|
+
}
|
|
716
|
+
if ((v && typeof v === "object" && !Object.keys(v).length) || v === null || v === undefined) {
|
|
637
717
|
delete object[k];
|
|
638
718
|
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
719
|
+
});
|
|
720
|
+
}
|
|
641
721
|
return object;
|
|
642
722
|
};
|
|
643
723
|
|
package/main.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cisco-perfmon",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "A library to pull Perfmon data from Cisco VOS applications via SOAP",
|
|
5
5
|
"main": "main.js",
|
|
6
|
+
"module": "main.mjs",
|
|
7
|
+
"types": "types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./main.mjs",
|
|
11
|
+
"require": "./main.js",
|
|
12
|
+
"types": "./types/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
6
15
|
"scripts": {
|
|
7
16
|
"test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_ENV=test node ./test/tests.js",
|
|
8
17
|
"development": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_ENV=development node ./test/tests.js",
|
|
@@ -20,7 +29,7 @@
|
|
|
20
29
|
"cucm",
|
|
21
30
|
"vos"
|
|
22
31
|
],
|
|
23
|
-
"author": "
|
|
32
|
+
"author": "sieteunoseis <jeremy.worden@gmail.com>",
|
|
24
33
|
"license": "MIT",
|
|
25
34
|
"bugs": {
|
|
26
35
|
"url": "https://github.com/sieteunoseis/cisco-perfmon/issues"
|
|
@@ -28,13 +37,12 @@
|
|
|
28
37
|
"homepage": "https://github.com/sieteunoseis/cisco-perfmon#readme",
|
|
29
38
|
"dependencies": {
|
|
30
39
|
"fetch-retry": "^6.0.0",
|
|
31
|
-
"
|
|
32
|
-
"xml2js": "*"
|
|
40
|
+
"xml2js": "^0.6.0"
|
|
33
41
|
},
|
|
34
42
|
"devDependencies": {
|
|
35
|
-
"dotenv": "
|
|
36
|
-
"envalid": "^8.
|
|
37
|
-
"jsdoc-to-markdown": "^9.0
|
|
43
|
+
"dotenv": "^17.0.0",
|
|
44
|
+
"envalid": "^8.1.0",
|
|
45
|
+
"jsdoc-to-markdown": "^9.1.0",
|
|
38
46
|
"p-limit": "^3.1.0"
|
|
39
47
|
}
|
|
40
48
|
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
export interface PerfmonOptions {
|
|
2
|
+
/** Max number of retries on network/HTTP errors. Defaults to PM_RETRY env var or 3. */
|
|
3
|
+
retries?: number;
|
|
4
|
+
/** Delay in ms between retries. Defaults to PM_RETRY_DELAY env var or 5000. */
|
|
5
|
+
retryDelay?: number;
|
|
6
|
+
Cookie?: string;
|
|
7
|
+
[key: string]: string | number | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PerfmonResult {
|
|
11
|
+
cookie: string;
|
|
12
|
+
results: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PerfmonCounterResult extends PerfmonResult {
|
|
16
|
+
object: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CounterData {
|
|
20
|
+
host: string;
|
|
21
|
+
object: string;
|
|
22
|
+
instance: string;
|
|
23
|
+
counter: string;
|
|
24
|
+
value: string;
|
|
25
|
+
cstatus: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CounterInfo {
|
|
29
|
+
Name: string;
|
|
30
|
+
MultiInstance: string;
|
|
31
|
+
ArrayOfCounter?: {
|
|
32
|
+
Counter: Array<{
|
|
33
|
+
Name: string;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CounterDescription {
|
|
39
|
+
Description: string;
|
|
40
|
+
Name: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Counter {
|
|
44
|
+
host: string;
|
|
45
|
+
object: string;
|
|
46
|
+
instance?: string;
|
|
47
|
+
counter: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
declare class perfMonService {
|
|
51
|
+
constructor(
|
|
52
|
+
host: string,
|
|
53
|
+
username: string,
|
|
54
|
+
password: string,
|
|
55
|
+
options?: PerfmonOptions,
|
|
56
|
+
retry?: boolean
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get the current stored cookie
|
|
61
|
+
*/
|
|
62
|
+
getCookie(): string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Set a cookie to be used for subsequent requests
|
|
66
|
+
*/
|
|
67
|
+
setCookie(cookie: string): void;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Collect counter data without a session
|
|
71
|
+
*/
|
|
72
|
+
collectCounterData(
|
|
73
|
+
host: string,
|
|
74
|
+
object: string
|
|
75
|
+
): Promise<{
|
|
76
|
+
cookie: string;
|
|
77
|
+
object: string;
|
|
78
|
+
results: CounterData[] | CounterData | string;
|
|
79
|
+
}>;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Collect data for an open session
|
|
83
|
+
*/
|
|
84
|
+
collectSessionData(
|
|
85
|
+
SessionHandle: string
|
|
86
|
+
): Promise<{
|
|
87
|
+
cookie: string;
|
|
88
|
+
results: CounterData[] | CounterData | string;
|
|
89
|
+
}>;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* List available counters on a host
|
|
93
|
+
*/
|
|
94
|
+
listCounter(
|
|
95
|
+
host: string,
|
|
96
|
+
filtered?: string[]
|
|
97
|
+
): Promise<{
|
|
98
|
+
cookie: string;
|
|
99
|
+
results: CounterInfo[] | string;
|
|
100
|
+
}>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* List instances of a perfmon object on a host
|
|
104
|
+
*/
|
|
105
|
+
listInstance(
|
|
106
|
+
host: string,
|
|
107
|
+
object: string
|
|
108
|
+
): Promise<{
|
|
109
|
+
cookie: string;
|
|
110
|
+
object: string;
|
|
111
|
+
results: any[] | string;
|
|
112
|
+
}>;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Open a perfmon session
|
|
116
|
+
*/
|
|
117
|
+
openSession(): Promise<{
|
|
118
|
+
cookie: string;
|
|
119
|
+
results: string;
|
|
120
|
+
}>;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Close a perfmon session
|
|
124
|
+
*/
|
|
125
|
+
closeSession(
|
|
126
|
+
sessionHandle: string
|
|
127
|
+
): Promise<{
|
|
128
|
+
cookie: string;
|
|
129
|
+
results: "success";
|
|
130
|
+
}>;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Add counter(s) to a session
|
|
134
|
+
*/
|
|
135
|
+
addCounter(
|
|
136
|
+
sessionHandle: string,
|
|
137
|
+
counter: Counter | Counter[]
|
|
138
|
+
): Promise<{
|
|
139
|
+
cookie: string;
|
|
140
|
+
results: "success";
|
|
141
|
+
}>;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Remove counter(s) from a session
|
|
145
|
+
*/
|
|
146
|
+
removeCounter(
|
|
147
|
+
sessionHandle: string,
|
|
148
|
+
counter: Counter | Counter[]
|
|
149
|
+
): Promise<{
|
|
150
|
+
cookie: string;
|
|
151
|
+
results: "success";
|
|
152
|
+
}>;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get counter description
|
|
156
|
+
*/
|
|
157
|
+
queryCounterDescription(
|
|
158
|
+
object: Counter
|
|
159
|
+
): Promise<{
|
|
160
|
+
cookie: string;
|
|
161
|
+
object: string;
|
|
162
|
+
results: CounterDescription | string;
|
|
163
|
+
}>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export = perfMonService;
|