cisco-perfmon 1.5.2 → 1.6.0

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>`;
@@ -99,6 +117,8 @@ var XML_REMOVE_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schem
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
+
102
122
  this._OPTIONS = {
103
123
  retryOn: async function (attempt, error, response) {
104
124
  if (!retry) {
@@ -107,6 +127,23 @@ class perfMonService {
107
127
  if (attempt > (process.env.PM_RETRY ? parseInt(process.env.PM_RETRY) : 3)) {
108
128
  return false;
109
129
  }
130
+
131
+ // Check for SOAP-level rate limiting (CUCM 80 req/min limit)
132
+ if (response && response.status === 500) {
133
+ try {
134
+ const clonedResponse = response.clone();
135
+ const body = await clonedResponse.text();
136
+ if (body.includes("Exceeded allowed rate for Perfmon")) {
137
+ const backoff = RATE_LIMIT_DELAYS[Math.min(attempt, RATE_LIMIT_DELAYS.length - 1)];
138
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
139
+ await delay(backoff);
140
+ return true;
141
+ }
142
+ } catch (e) {
143
+ // If we can't read the body, fall through to normal retry
144
+ }
145
+ }
146
+
110
147
  // retry on any network error, or 4xx or 5xx status codes
111
148
  if (error !== null || response.status >= 400) {
112
149
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -128,6 +165,28 @@ class perfMonService {
128
165
  }
129
166
 
130
167
  this._HOST = host;
168
+ this._COOKIE = "";
169
+ }
170
+ /**
171
+ * Get the current stored cookie
172
+ *
173
+ * @getCookie
174
+ * @memberof perfMonService
175
+ * @returns {string} returns the stored cookie string.
176
+ */
177
+ getCookie() {
178
+ return this._COOKIE;
179
+ }
180
+ /**
181
+ * Set a cookie to be used for subsequent requests
182
+ *
183
+ * @setCookie
184
+ * @memberof perfMonService
185
+ * @param {string} cookie - The cookie string to set.
186
+ */
187
+ setCookie(cookie) {
188
+ this._COOKIE = cookie;
189
+ this._OPTIONS.headers.Cookie = cookie;
131
190
  }
132
191
  /**
133
192
  * Post Fetch using Cisco PerfMon API
@@ -147,7 +206,7 @@ class perfMonService {
147
206
  try {
148
207
  let options = this._OPTIONS;
149
208
  let server = this._HOST;
150
- let XML = util.format(XML_COLLECT_COUNTER_ENVELOPE, host, object);
209
+ let XML = XML_COLLECT_COUNTER_ENVELOPE(escapeXml(host), escapeXml(object));
151
210
  let soapBody = Buffer.from(XML);
152
211
  options.body = soapBody;
153
212
  options.SOAPAction = `perfmonCollectCounterData`;
@@ -161,6 +220,12 @@ class perfMonService {
161
220
  };
162
221
 
163
222
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
223
+ if (promiseResults.cookie) {
224
+ this.setCookie(promiseResults.cookie);
225
+ }
226
+ if (promiseResults.cookie) {
227
+ this.setCookie(promiseResults.cookie);
228
+ }
164
229
 
165
230
  let output = await parseXml(await response.text());
166
231
  // Remove unnecessary keys
@@ -198,9 +263,9 @@ class perfMonService {
198
263
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
199
264
  */
200
265
  async collectSessionData(SessionHandle) {
201
- let options = this._OPTIONS;
266
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
202
267
  let server = this._HOST;
203
- let XML = util.format(XML_COLLECT_SESSION_ENVELOPE, SessionHandle);
268
+ let XML = XML_COLLECT_SESSION_ENVELOPE(SessionHandle);
204
269
  let soapBody = Buffer.from(XML);
205
270
  options.body = soapBody;
206
271
  options.SOAPAction = `perfmonCollectSessionData`;
@@ -213,6 +278,9 @@ class perfMonService {
213
278
  };
214
279
 
215
280
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
281
+ if (promiseResults.cookie) {
282
+ this.setCookie(promiseResults.cookie);
283
+ }
216
284
 
217
285
  let output = await parseXml(await response.text());
218
286
  // Remove unnecessary keys
@@ -246,9 +314,9 @@ class perfMonService {
246
314
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
247
315
  */
248
316
  async listCounter(host, filtered = []) {
249
- let options = this._OPTIONS;
317
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
250
318
  let server = this._HOST;
251
- let XML = util.format(XML_LIST_COUNTER_ENVELOPE, host);
319
+ let XML = XML_LIST_COUNTER_ENVELOPE(escapeXml(host));
252
320
  let soapBody = Buffer.from(XML);
253
321
  options.body = soapBody;
254
322
  options.SOAPAction = `perfmonListCounter`;
@@ -261,6 +329,9 @@ class perfMonService {
261
329
  };
262
330
 
263
331
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
332
+ if (promiseResults.cookie) {
333
+ this.setCookie(promiseResults.cookie);
334
+ }
264
335
 
265
336
  let output = await parseXml(await response.text());
266
337
  // Remove unnecessary keys
@@ -299,9 +370,9 @@ class perfMonService {
299
370
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
300
371
  */
301
372
  async listInstance(host, object) {
302
- let options = this._OPTIONS;
373
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
303
374
  let server = this._HOST;
304
- let XML = util.format(XML_LIST_INSTANCE_ENVELOPE, host, object);
375
+ let XML = XML_LIST_INSTANCE_ENVELOPE(escapeXml(host), escapeXml(object));
305
376
  let soapBody = Buffer.from(XML);
306
377
  options.body = soapBody;
307
378
  options.SOAPAction = `perfmonListInstance`;
@@ -315,6 +386,9 @@ class perfMonService {
315
386
  };
316
387
 
317
388
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
389
+ if (promiseResults.cookie) {
390
+ this.setCookie(promiseResults.cookie);
391
+ }
318
392
 
319
393
  let output = await parseXml(await response.text());
320
394
  // Remove unnecessary keys
@@ -331,11 +405,7 @@ class perfMonService {
331
405
 
332
406
  // If the results are not an array, we make it an array.
333
407
  if (!Array.isArray(promiseResults.results)) {
334
- var temp = promiseResults.results;
335
- promiseResults = {
336
- results: [],
337
- };
338
- promiseResults.results.push(temp);
408
+ promiseResults.results = [promiseResults.results];
339
409
  }
340
410
  return promiseResults;
341
411
  } else {
@@ -356,9 +426,9 @@ class perfMonService {
356
426
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
357
427
  */
358
428
  async openSession() {
359
- let options = this._OPTIONS;
429
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
360
430
  let server = this._HOST;
361
- let XML = util.format(XML_OPEN_SESSION_ENVELOPE);
431
+ let XML = XML_OPEN_SESSION_ENVELOPE();
362
432
  let soapBody = Buffer.from(XML);
363
433
  options.body = soapBody;
364
434
  options.SOAPAction = `perfmonOpenSession`;
@@ -371,6 +441,9 @@ class perfMonService {
371
441
  };
372
442
 
373
443
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
444
+ if (promiseResults.cookie) {
445
+ this.setCookie(promiseResults.cookie);
446
+ }
374
447
 
375
448
  let output = await parseXml(await response.text());
376
449
  // Remove unnecessary keys
@@ -404,9 +477,9 @@ class perfMonService {
404
477
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
405
478
  */
406
479
  async closeSession(sessionHandle) {
407
- let options = this._OPTIONS;
480
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
408
481
  let server = this._HOST;
409
- let XML = util.format(XML_CLOSE_SESSION_ENVELOPE, sessionHandle);
482
+ let XML = XML_CLOSE_SESSION_ENVELOPE(sessionHandle);
410
483
  let soapBody = Buffer.from(XML);
411
484
  options.body = soapBody;
412
485
  options.SOAPAction = `perfmonCloseSession`;
@@ -419,6 +492,9 @@ class perfMonService {
419
492
  };
420
493
 
421
494
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
495
+ if (promiseResults.cookie) {
496
+ this.setCookie(promiseResults.cookie);
497
+ }
422
498
 
423
499
  let output = await parseXml(await response.text());
424
500
  // Remove unnecessary keys
@@ -451,16 +527,10 @@ class perfMonService {
451
527
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
452
528
  */
453
529
  async addCounter(sessionHandle, counter) {
454
- let options = this._OPTIONS;
530
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
455
531
  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);
532
+ let counterStr = Array.isArray(counter) ? counter.map(buildCounterStr).join("") : buildCounterStr(counter);
533
+ let XML = XML_ADD_COUNTER_ENVELOPE(sessionHandle, counterStr);
464
534
  let soapBody = Buffer.from(XML);
465
535
  options.body = soapBody;
466
536
  options.SOAPAction = `perfmonAddCounter`;
@@ -473,6 +543,9 @@ class perfMonService {
473
543
  };
474
544
 
475
545
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
546
+ if (promiseResults.cookie) {
547
+ this.setCookie(promiseResults.cookie);
548
+ }
476
549
 
477
550
  let output = await parseXml(await response.text());
478
551
  // Remove unnecessary keys
@@ -505,17 +578,10 @@ class perfMonService {
505
578
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
506
579
  */
507
580
  async removeCounter(sessionHandle, counter) {
508
- let options = this._OPTIONS;
581
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
509
582
  let server = this._HOST;
510
-
511
- let counterStr;
512
- if (Array.isArray(counter)) {
513
- counter.forEach((counter) => (counterStr += "<soap:Counter>" + "<soap:Name>" + "\\\\" + counter.host + "\\" + (counter.instance ? `${counter.object}(${counter.instance})` : counter.object) + "\\" + counter.counter + "</soap:Name>" + "</soap:Counter>"));
514
- } else {
515
- counterStr = "<soap:Counter>" + "<soap:Name>" + "\\\\" + counter.host + "\\" + (counter.instance ? `${counter.object}(${counter.instance})` : counter.object) + "\\" + counter.counter + "</soap:Name>" + "</soap:Counter>";
516
- }
517
-
518
- let XML = util.format(XML_REMOVE_COUNTER_ENVELOPE, sessionHandle, counterStr);
583
+ let counterStr = Array.isArray(counter) ? counter.map(buildCounterStr).join("") : buildCounterStr(counter);
584
+ let XML = XML_REMOVE_COUNTER_ENVELOPE(sessionHandle, counterStr);
519
585
  let soapBody = Buffer.from(XML);
520
586
  options.body = soapBody;
521
587
  options.SOAPAction = `perfmonRemoveCounter`;
@@ -528,6 +594,9 @@ class perfMonService {
528
594
  };
529
595
 
530
596
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
597
+ if (promiseResults.cookie) {
598
+ this.setCookie(promiseResults.cookie);
599
+ }
531
600
 
532
601
  let output = await parseXml(await response.text());
533
602
  // Remove unnecessary keys
@@ -559,10 +628,10 @@ class perfMonService {
559
628
  * @returns {object} returns JSON via a Promise. JSON contains Session Cookie (If availible) and Results.
560
629
  */
561
630
  async queryCounterDescription(object) {
562
- let options = this._OPTIONS;
631
+ let options = { ...this._OPTIONS, headers: { ...this._OPTIONS.headers } };
563
632
  let server = this._HOST;
564
- let counterStr = "<soap:Counter>" + "\\\\" + object.host + "\\" + (object.instance ? `${object.object}(${object.instance})` : object.object) + "\\" + object.counter + "</soap:Counter>";
565
- let XML = util.format(XML_QUERY_COUNTER_ENVELOPE, counterStr);
633
+ let counterStr = buildCounterStr(object);
634
+ let XML = XML_QUERY_COUNTER_ENVELOPE(counterStr);
566
635
  let soapBody = Buffer.from(XML);
567
636
  options.body = soapBody;
568
637
  options.SOAPAction = `perfmonQueryCounterDescription`;
@@ -576,6 +645,9 @@ class perfMonService {
576
645
  };
577
646
 
578
647
  promiseResults.cookie = response.headers.get("set-cookie") ? response.headers.get("set-cookie") : "";
648
+ if (promiseResults.cookie) {
649
+ this.setCookie(promiseResults.cookie);
650
+ }
579
651
 
580
652
  let output = await parseXml(await response.text());
581
653
  // Remove unnecessary keys
@@ -627,18 +699,26 @@ const removeKeys = (obj, keys) => {
627
699
  };
628
700
 
629
701
  const clean = (object) => {
630
- Object.entries(object).forEach(([k, v]) => {
631
- if (v && typeof v === "object") {
632
- clean(v);
702
+ if (Array.isArray(object)) {
703
+ for (let i = object.length - 1; i >= 0; i--) {
704
+ const v = object[i];
705
+ if (v && typeof v === "object") {
706
+ clean(v);
707
+ }
708
+ if ((v && typeof v === "object" && !Object.keys(v).length) || v === null || v === undefined) {
709
+ object.splice(i, 1);
710
+ }
633
711
  }
634
- if ((v && typeof v === "object" && !Object.keys(v).length) || v === null || v === undefined) {
635
- if (Array.isArray(object)) {
636
- object.splice(k, 1);
637
- } else {
712
+ } else {
713
+ Object.entries(object).forEach(([k, v]) => {
714
+ if (v && typeof v === "object") {
715
+ clean(v);
716
+ }
717
+ if ((v && typeof v === "object" && !Object.keys(v).length) || v === null || v === undefined) {
638
718
  delete object[k];
639
719
  }
640
- }
641
- });
720
+ });
721
+ }
642
722
  return object;
643
723
  };
644
724
 
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.2",
3
+ "version": "1.6.0",
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,162 @@
1
+ export interface PerfmonOptions {
2
+ Cookie?: string;
3
+ [key: string]: string | undefined;
4
+ }
5
+
6
+ export interface PerfmonResult {
7
+ cookie: string;
8
+ results: any;
9
+ }
10
+
11
+ export interface PerfmonCounterResult extends PerfmonResult {
12
+ object: string;
13
+ }
14
+
15
+ export interface CounterData {
16
+ host: string;
17
+ object: string;
18
+ instance: string;
19
+ counter: string;
20
+ value: string;
21
+ cstatus: string;
22
+ }
23
+
24
+ export interface CounterInfo {
25
+ Name: string;
26
+ MultiInstance: string;
27
+ ArrayOfCounter?: {
28
+ Counter: Array<{
29
+ Name: string;
30
+ }>;
31
+ };
32
+ }
33
+
34
+ export interface CounterDescription {
35
+ Description: string;
36
+ Name: string;
37
+ }
38
+
39
+ export interface Counter {
40
+ host: string;
41
+ object: string;
42
+ instance?: string;
43
+ counter: string;
44
+ }
45
+
46
+ declare class perfMonService {
47
+ constructor(
48
+ host: string,
49
+ username: string,
50
+ password: string,
51
+ options?: PerfmonOptions,
52
+ retry?: boolean
53
+ );
54
+
55
+ /**
56
+ * Get the current stored cookie
57
+ */
58
+ getCookie(): string;
59
+
60
+ /**
61
+ * Set a cookie to be used for subsequent requests
62
+ */
63
+ setCookie(cookie: string): void;
64
+
65
+ /**
66
+ * Collect counter data without a session
67
+ */
68
+ collectCounterData(
69
+ host: string,
70
+ object: string
71
+ ): Promise<{
72
+ cookie: string;
73
+ object: string;
74
+ results: CounterData[] | CounterData | string;
75
+ }>;
76
+
77
+ /**
78
+ * Collect data for an open session
79
+ */
80
+ collectSessionData(
81
+ SessionHandle: string
82
+ ): Promise<{
83
+ cookie: string;
84
+ results: CounterData[] | CounterData | string;
85
+ }>;
86
+
87
+ /**
88
+ * List available counters on a host
89
+ */
90
+ listCounter(
91
+ host: string,
92
+ filtered?: string[]
93
+ ): Promise<{
94
+ cookie: string;
95
+ results: CounterInfo[] | string;
96
+ }>;
97
+
98
+ /**
99
+ * List instances of a perfmon object on a host
100
+ */
101
+ listInstance(
102
+ host: string,
103
+ object: string
104
+ ): Promise<{
105
+ cookie: string;
106
+ object: string;
107
+ results: any[] | string;
108
+ }>;
109
+
110
+ /**
111
+ * Open a perfmon session
112
+ */
113
+ openSession(): Promise<{
114
+ cookie: string;
115
+ results: string;
116
+ }>;
117
+
118
+ /**
119
+ * Close a perfmon session
120
+ */
121
+ closeSession(
122
+ sessionHandle: string
123
+ ): Promise<{
124
+ cookie: string;
125
+ results: "success";
126
+ }>;
127
+
128
+ /**
129
+ * Add counter(s) to a session
130
+ */
131
+ addCounter(
132
+ sessionHandle: string,
133
+ counter: Counter | Counter[]
134
+ ): Promise<{
135
+ cookie: string;
136
+ results: "success";
137
+ }>;
138
+
139
+ /**
140
+ * Remove counter(s) from a session
141
+ */
142
+ removeCounter(
143
+ sessionHandle: string,
144
+ counter: Counter | Counter[]
145
+ ): Promise<{
146
+ cookie: string;
147
+ results: "success";
148
+ }>;
149
+
150
+ /**
151
+ * Get counter description
152
+ */
153
+ queryCounterDescription(
154
+ object: Counter
155
+ ): Promise<{
156
+ cookie: string;
157
+ object: string;
158
+ results: CounterDescription | string;
159
+ }>;
160
+ }
161
+
162
+ export = perfMonService;