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.
@@ -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
- var XML_ADD_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
6
+ const escapeXml = (str) => {
7
+ if (typeof str !== "string") return str;
8
+ return str
9
+ .replace(/&/g, "&amp;")
10
+ .replace(/</g, "&lt;")
11
+ .replace(/>/g, "&gt;")
12
+ .replace(/"/g, "&quot;")
13
+ .replace(/'/g, "&apos;");
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>%s</soap:SessionHandle>
12
- <soap:ArrayOfCounter>%s</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
- var XML_CLOSE_SESSION_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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>%s</soap:SessionHandle>
39
+ <soap:SessionHandle>${sessionHandle}</soap:SessionHandle>
22
40
  </soap:perfmonCloseSession>
23
41
  </soapenv:Body>
24
42
  </soapenv:Envelope>`;
25
43
 
26
- var XML_COLLECT_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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>%s</soap:Host>
31
- <soap:Object>%s</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
- var XML_COLLECT_SESSION_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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>%s</soap:SessionHandle>
58
+ <soap:SessionHandle>${sessionHandle}</soap:SessionHandle>
41
59
  </soap:perfmonCollectSessionData>
42
60
  </soapenv:Body>
43
61
  </soapenv:Envelope>`;
44
62
 
45
- var XML_LIST_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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>%s</soap:Host>
67
+ <soap:Host>${host}</soap:Host>
50
68
  </soap:perfmonListCounter>
51
69
  </soapenv:Body>
52
70
  </soapenv:Envelope>`;
53
71
 
54
- var XML_LIST_INSTANCE_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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>%s</soap:Host>
59
- <soap:Object>%s</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
- var XML_OPEN_SESSION_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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
- var XML_QUERY_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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>%s</soap:perfmonQueryCounterDescription>
92
+ <soap:perfmonQueryCounterDescription>${counter}</soap:perfmonQueryCounterDescription>
75
93
  </soapenv:Body>
76
94
  </soapenv:Envelope>`;
77
95
 
78
- var XML_REMOVE_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
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>%s</soap:SessionHandle>
83
- <soap:ArrayOfCounter>%s</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 - Additional headers to add to the request. Useful for adding cookies for SSO sessions.
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 > (process.env.PM_RETRY ? parseInt(process.env.PM_RETRY) : 3)) {
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(process.env.PM_RETRY_DELAY ? parseInt(process.env.PM_RETRY_DELAY) : 5000);
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 = util.format(XML_COLLECT_COUNTER_ENVELOPE, host, object);
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 = util.format(XML_COLLECT_SESSION_ENVELOPE, SessionHandle);
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 = util.format(XML_LIST_COUNTER_ENVELOPE, host);
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 = util.format(XML_LIST_INSTANCE_ENVELOPE, host, object);
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
- var temp = promiseResults.results;
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 = util.format(XML_OPEN_SESSION_ENVELOPE);
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 = util.format(XML_CLOSE_SESSION_ENVELOPE, sessionHandle);
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
- // Build the counter string
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
- if (Array.isArray(counter)) {
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 = "<soap:Counter>" + "\\\\" + object.host + "\\" + (object.instance ? `${object.object}(${object.instance})` : object.object) + "\\" + object.counter + "</soap:Counter>";
564
- let XML = util.format(XML_QUERY_COUNTER_ENVELOPE, counterStr);
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
- Object.entries(object).forEach(([k, v]) => {
630
- if (v && typeof v === "object") {
631
- clean(v);
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
- if ((v && typeof v === "object" && !Object.keys(v).length) || v === null || v === undefined) {
634
- if (Array.isArray(object)) {
635
- object.splice(k, 1);
636
- } else {
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
@@ -0,0 +1,5 @@
1
+ import { createRequire } from "module";
2
+ const require = createRequire(import.meta.url);
3
+ const perfMonService = require("./main.js");
4
+
5
+ export default perfMonService;
package/package.json CHANGED
@@ -1,8 +1,17 @@
1
1
  {
2
2
  "name": "cisco-perfmon",
3
- "version": "1.5.3",
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": "Jeremy Worden <jeremy@automate.builders> (https://automate.builders/)",
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
- "util": "*",
32
- "xml2js": "*"
40
+ "xml2js": "^0.6.0"
33
41
  },
34
42
  "devDependencies": {
35
- "dotenv": "*",
36
- "envalid": "^8.0.0",
37
- "jsdoc-to-markdown": "^9.0.4",
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
  }
@@ -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;