cisco-perfmon 1.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Jeremy Worden
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Cisco RisPort Library
2
+
3
+ Simple library to pull Perfmon stats from a Cisco CUCM via SOAP.
4
+
5
+ Perfmon information can be found at
6
+ [PerfMon API Reference](https://developer.cisco.com/docs/sxml/#!perfmon-api-reference).
7
+
8
+ ## Installation
9
+
10
+ Using npm:
11
+
12
+ ```javascript
13
+ npm i -g npm
14
+ npm i --save cisco-perfmon
15
+ ```
16
+
17
+ ## Requirements
18
+
19
+ This package uses the built in Fetch API of Node. This feature was first introduced in Node v16.15.0. You may need to enable expermential vm module. Also you can disable warnings with an optional enviromental variable.
20
+
21
+ Also if you are using self signed certificates on Cisco VOS products you may need to disable TLS verification. This makes TLS, and HTTPS by extension, insecure. The use of this environment variable is strongly discouraged. Please only do this in a lab enviroment.
22
+
23
+ Suggested enviromental variables:
24
+
25
+ ```env
26
+ NODE_OPTIONS=--experimental-vm-modules
27
+ NODE_NO_WARNINGS=1
28
+ NODE_TLS_REJECT_UNAUTHORIZED=0
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ Note: Rather than use string with backslashes i.e. "\\cucm3\Cisco CallManager\CallsActive", opted to pass these via JSON to the functions. See below:
34
+
35
+ ```javascript
36
+ const perfMonService = require("../main");
37
+
38
+ // Set up new PerfMon service
39
+ let service = new perfMonService(
40
+ "10.10.20.1",
41
+ "administrator",
42
+ "ciscopsdt"
43
+ );
44
+
45
+ var counterObj = {
46
+ host: "cucm01-pub",
47
+ object: "Cisco CallManager",
48
+ counter: "CallsActive",
49
+ };
50
+
51
+ console.log("Let's get a description of our counter.");
52
+ service
53
+ .queryCounterDescription(counterObj)
54
+ .then((results) => {
55
+ console.log("queryCounterDescription", results);
56
+ })
57
+ .catch((error) => {
58
+ console.log(error);
59
+ });
60
+ ```
61
+
62
+ ## Examples
63
+
64
+ ```javascript
65
+ npm run test
66
+ ```
67
+
68
+ Note: Test are using Cisco's DevNet sandbox information. Find more information here: [Cisco DevNet](https://devnetsandbox.cisco.com/)
package/main.js ADDED
@@ -0,0 +1,856 @@
1
+ const util = require("util");
2
+ const parseString = require("xml2js").parseString;
3
+ const stripPrefix = require("xml2js").processors.stripPrefix;
4
+
5
+ var XML_ADD_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
6
+ <soapenv:Header/>
7
+ <soapenv:Body>
8
+ <soap:perfmonAddCounter>
9
+ <soap:SessionHandle>%s</soap:SessionHandle>
10
+ <soap:ArrayOfCounter>%s</soap:ArrayOfCounter>
11
+ </soap:perfmonAddCounter>
12
+ </soapenv:Body>
13
+ </soapenv:Envelope>`;
14
+
15
+ var XML_CLOSE_SESSION_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
16
+ <soapenv:Header/>
17
+ <soapenv:Body>
18
+ <soap:perfmonCloseSession>
19
+ <soap:SessionHandle>%s</soap:SessionHandle>
20
+ </soap:perfmonCloseSession>
21
+ </soapenv:Body>
22
+ </soapenv:Envelope>`;
23
+
24
+ var XML_COLLECT_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
25
+ <soapenv:Header/>
26
+ <soapenv:Body>
27
+ <soap:perfmonCollectCounterData>
28
+ <soap:Host>%s</soap:Host>
29
+ <soap:Object>%s</soap:Object>
30
+ </soap:perfmonCollectCounterData>
31
+ </soapenv:Body>
32
+ </soapenv:Envelope>`;
33
+
34
+ var XML_COLLECT_SESSION_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
35
+ <soapenv:Header/>
36
+ <soapenv:Body>
37
+ <soap:perfmonCollectSessionData>
38
+ <soap:SessionHandle>%s</soap:SessionHandle>
39
+ </soap:perfmonCollectSessionData>
40
+ </soapenv:Body>
41
+ </soapenv:Envelope>`;
42
+
43
+ var XML_LIST_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
44
+ <soapenv:Header/>
45
+ <soapenv:Body>
46
+ <soap:perfmonListCounter>
47
+ <soap:Host>%s</soap:Host>
48
+ </soap:perfmonListCounter>
49
+ </soapenv:Body>
50
+ </soapenv:Envelope>`;
51
+
52
+ var XML_LIST_INSTANCE_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
53
+ <soapenv:Header/>
54
+ <soapenv:Body>
55
+ <soap:perfmonListInstance>
56
+ <soap:Host>%s</soap:Host>
57
+ <soap:Object>%s</soap:Object>
58
+ </soap:perfmonListInstance>
59
+ </soapenv:Body>
60
+ </soapenv:Envelope>`;
61
+
62
+ var XML_OPEN_SESSION_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
63
+ <soapenv:Header/>
64
+ <soapenv:Body>
65
+ <soap:perfmonOpenSession/>
66
+ </soapenv:Body>
67
+ </soapenv:Envelope>`;
68
+
69
+ var XML_QUERY_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
70
+ <soapenv:Header/>
71
+ <soapenv:Body>
72
+ <soap:perfmonQueryCounterDescription>%s</soap:perfmonQueryCounterDescription>
73
+ </soapenv:Body>
74
+ </soapenv:Envelope>`;
75
+
76
+ var XML_REMOVE_COUNTER_ENVELOPE = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.cisco.com/ast/soap">
77
+ <soapenv:Header/>
78
+ <soapenv:Body>
79
+ <soap:perfmonRemoveCounter>
80
+ <soap:SessionHandle>%s</soap:SessionHandle>
81
+ <soap:ArrayOfCounter>%s</soap:ArrayOfCounter>
82
+ </soap:perfmonRemoveCounter>
83
+ </soapenv:Body>
84
+ </soapenv:Envelope>`;
85
+
86
+ /**
87
+ * Cisco Perfmon Service
88
+ * This is a service class that uses fetch and promises to pull Perfmon data from Cisco CUCM
89
+ *
90
+ *
91
+ * @class perfMonService
92
+ */
93
+ class perfMonService {
94
+ constructor(host, username, password) {
95
+ this._OPTIONS = {
96
+ method: "POST",
97
+ headers: {
98
+ Authorization:
99
+ "Basic " + Buffer.from(username + ":" + password).toString("base64"),
100
+ "Content-Type": "text/xml;charset=UTF-8",
101
+ },
102
+ };
103
+ this._HOST = host;
104
+ }
105
+ /**
106
+ * Post Fetch using Cisco PerfMon API
107
+ *
108
+ * @collectCounterData
109
+ var service = new perfMonService();
110
+ service.collectCounterData().then((success => {
111
+ console.log(success);
112
+ }))
113
+ * @memberof perfMonService
114
+ * @returns {promise} returns a Promise
115
+ */
116
+ collectCounterData(host, object) {
117
+ var XML;
118
+ var options = this._OPTIONS;
119
+ options.SOAPAction = `perfmonCollectCounterData`;
120
+ var server = this._HOST;
121
+
122
+ XML = util.format(XML_COLLECT_COUNTER_ENVELOPE, host, object);
123
+
124
+ var soapBody = Buffer.from(XML);
125
+ options.body = soapBody;
126
+
127
+ return new Promise((resolve, reject) => {
128
+ // We fetch the API endpoint
129
+ fetch(
130
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
131
+ options
132
+ )
133
+ .then(async (response) => {
134
+ // console.log(response.body);
135
+ var data = []; // create an array to save chunked data from server
136
+ // response.body is a ReadableStream
137
+ const reader = response.body.getReader();
138
+ for await (const chunk of readChunks(reader)) {
139
+ data.push(Buffer.from(chunk));
140
+ }
141
+ var buffer = Buffer.concat(data); // create buffer of data
142
+ let xmlOutput = buffer.toString("binary").trim();
143
+ let output = await parseXml(xmlOutput);
144
+ // Remove unnecessary keys
145
+ removeKeys(output, "$");
146
+
147
+ if (keyExists(output, "perfmonCollectCounterDataReturn")) {
148
+ var returnResults =
149
+ output.Body.perfmonCollectCounterDataResponse
150
+ .perfmonCollectCounterDataReturn;
151
+ if (returnResults) {
152
+ var newOutput;
153
+ if (Array.isArray(returnResults)) {
154
+ newOutput = returnResults.map((item) => {
155
+ let arr = item.Name.split("\\").filter((element) => element);
156
+ return {
157
+ host: arr[0],
158
+ object: arr[1],
159
+ counter: arr[2],
160
+ value: item.Value,
161
+ cstatus: item.CStatus,
162
+ };
163
+ });
164
+ } else {
165
+ let arr = returnResults.Name.split("\\").filter(
166
+ (element) => element
167
+ );
168
+ newOutput = {
169
+ host: arr[0],
170
+ object: arr[1],
171
+ counter: arr[2],
172
+ value: returnResults.Value,
173
+ cstatus: returnResults.CStatus,
174
+ };
175
+ }
176
+ resolve(clean(newOutput));
177
+ } else {
178
+ reject(output.Body.Fault);
179
+ }
180
+ } else {
181
+ reject({ response: "empty" });
182
+ }
183
+ })
184
+ .catch((error) => {
185
+ reject(error);
186
+ }); // catches the error and logs it
187
+ });
188
+ }
189
+ /**
190
+ * Post Fetch using Cisco PerfMon API
191
+ *
192
+ * @collectSessionData
193
+ var service = new perfMonService();
194
+ service.collectSessionData().then((success => {
195
+ console.log(success);
196
+ }))
197
+ * @memberof perfMonService
198
+ * @returns {promise} returns a Promise
199
+ */
200
+ collectSessionData(SessionHandle) {
201
+ var XML;
202
+ var options = this._OPTIONS;
203
+ options.SOAPAction = `perfmonCollectSessionData`;
204
+ var server = this._HOST;
205
+
206
+ XML = util.format(XML_COLLECT_SESSION_ENVELOPE, SessionHandle);
207
+
208
+ var soapBody = Buffer.from(XML);
209
+ options.body = soapBody;
210
+
211
+ return new Promise((resolve, reject) => {
212
+ // We fetch the API endpoint
213
+ fetch(
214
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
215
+ options
216
+ )
217
+ .then(async (response) => {
218
+ // console.log(response.body);
219
+ var data = []; // create an array to save chunked data from server
220
+ // response.body is a ReadableStream
221
+ const reader = response.body.getReader();
222
+ for await (const chunk of readChunks(reader)) {
223
+ data.push(Buffer.from(chunk));
224
+ }
225
+ var buffer = Buffer.concat(data); // create buffer of data
226
+ let xmlOutput = buffer.toString("binary").trim();
227
+ let output = await parseXml(xmlOutput);
228
+ // Remove unnecessary keys
229
+ removeKeys(output, "$");
230
+
231
+ if (keyExists(output, "perfmonCollectSessionDataReturn")) {
232
+ var returnResults =
233
+ output.Body.perfmonCollectSessionDataResponse
234
+ .perfmonCollectSessionDataReturn;
235
+
236
+ if (returnResults) {
237
+ var newOutput;
238
+ if (Array.isArray(returnResults)) {
239
+ newOutput = returnResults.map((item) => {
240
+ let arr = item.Name.split("\\").filter((element) => element);
241
+ return {
242
+ host: arr[0],
243
+ object: arr[1],
244
+ counter: arr[2],
245
+ value: item.Value,
246
+ cstatus: item.CStatus,
247
+ };
248
+ });
249
+ } else {
250
+ let arr = returnResults.Name.split("\\").filter(
251
+ (element) => element
252
+ );
253
+ newOutput = {
254
+ host: arr[0],
255
+ object: arr[1],
256
+ counter: arr[2],
257
+ value: returnResults.Value,
258
+ cstatus: returnResults.CStatus,
259
+ };
260
+ }
261
+ resolve(clean(newOutput));
262
+ } else {
263
+ reject(output.Body.Fault);
264
+ }
265
+ } else {
266
+ reject({ response: "empty" });
267
+ }
268
+ })
269
+ .catch((error) => {
270
+ reject(error);
271
+ }); // catches the error and logs it
272
+ });
273
+ }
274
+ /**
275
+ * Post Fetch using Cisco PerfMon API
276
+ *
277
+ * @listCounter
278
+ var service = new perfMonService();
279
+ service.listCounter().then((success => {
280
+ console.log(success);
281
+ }))
282
+ * @memberof perfMonService
283
+ * @returns {promise} returns a Promise
284
+ */
285
+ listCounter(host) {
286
+ var XML;
287
+ var options = this._OPTIONS;
288
+ options.SOAPAction = `perfmonListCounter`;
289
+ var server = this._HOST;
290
+
291
+ XML = util.format(XML_LIST_COUNTER_ENVELOPE, host);
292
+
293
+ var soapBody = Buffer.from(XML);
294
+ options.body = soapBody;
295
+
296
+ return new Promise((resolve, reject) => {
297
+ // We fetch the API endpoint
298
+ fetch(
299
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
300
+ options
301
+ )
302
+ .then(async (response) => {
303
+ var data = []; // create an array to save chunked data from server
304
+ // response.body is a ReadableStream
305
+ const reader = response.body.getReader();
306
+ for await (const chunk of readChunks(reader)) {
307
+ data.push(Buffer.from(chunk));
308
+ }
309
+ var buffer = Buffer.concat(data); // create buffer of data
310
+ let xmlOutput = buffer.toString("binary").trim();
311
+ let output = await parseXml(xmlOutput);
312
+ // Remove unnecessary keys
313
+ removeKeys(output, "$");
314
+
315
+ if (keyExists(output, "perfmonListCounterReturn")) {
316
+ var returnResults =
317
+ output.Body.perfmonListCounterResponse.perfmonListCounterReturn;
318
+
319
+ if (returnResults) {
320
+ resolve(clean(returnResults));
321
+ } else {
322
+ reject(output.Body.Fault);
323
+ }
324
+ } else {
325
+ reject({ response: "empty" });
326
+ }
327
+ })
328
+ .catch((error) => {
329
+ reject(error);
330
+ }); // catches the error and logs it
331
+ });
332
+ }
333
+ /**
334
+ * Post Fetch using Cisco PerfMon API
335
+ *
336
+ * @listInstance
337
+ var service = new perfMonService();
338
+ service.listInstance().then((success => {
339
+ console.log(success);
340
+ }))
341
+ * @memberof perfMonService
342
+ * @returns {promise} returns a Promise
343
+ */
344
+ listInstance(host, object) {
345
+ var XML;
346
+ var options = this._OPTIONS;
347
+ options.SOAPAction = `perfmonListInstance`;
348
+ var server = this._HOST;
349
+
350
+ XML = util.format(XML_LIST_INSTANCE_ENVELOPE, host, object);
351
+
352
+ var soapBody = Buffer.from(XML);
353
+ options.body = soapBody;
354
+
355
+ return new Promise((resolve, reject) => {
356
+ // We fetch the API endpoint
357
+ fetch(
358
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
359
+ options
360
+ )
361
+ .then(async (response) => {
362
+ var data = []; // create an array to save chunked data from server
363
+ // response.body is a ReadableStream
364
+ const reader = response.body.getReader();
365
+ for await (const chunk of readChunks(reader)) {
366
+ data.push(Buffer.from(chunk));
367
+ }
368
+ var buffer = Buffer.concat(data); // create buffer of data
369
+ let xmlOutput = buffer.toString("binary").trim();
370
+ let output = await parseXml(xmlOutput);
371
+ // Remove unnecessary keys
372
+ removeKeys(output, "$");
373
+
374
+ if (keyExists(output, "perfmonListInstanceReturn")) {
375
+ var returnResults =
376
+ output.Body.perfmonListInstanceResponse.perfmonListInstanceReturn;
377
+
378
+ if (returnResults) {
379
+ resolve(clean(returnResults));
380
+ } else {
381
+ reject(output.Body.Fault);
382
+ }
383
+ } else {
384
+ reject({ response: "empty" });
385
+ }
386
+ })
387
+ .catch((error) => {
388
+ reject(error);
389
+ }); // catches the error and logs it
390
+ });
391
+ }
392
+ /**
393
+ * Post Fetch using Cisco PerfMon API
394
+ *
395
+ * @openSession
396
+ var service = new perfMonService();
397
+ service.openSession().then((success => {
398
+ console.log(success);
399
+ }))
400
+ * @memberof perfMonService
401
+ * @returns {promise} returns a Promise
402
+ */
403
+ openSession() {
404
+ var XML;
405
+ var options = this._OPTIONS;
406
+ options.SOAPAction = `perfmonOpenSession`;
407
+ var server = this._HOST;
408
+ XML = util.format(XML_OPEN_SESSION_ENVELOPE);
409
+
410
+ var soapBody = Buffer.from(XML);
411
+ options.body = soapBody;
412
+
413
+ return new Promise((resolve, reject) => {
414
+ // We fetch the API endpoint
415
+ fetch(
416
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
417
+ options
418
+ )
419
+ .then(async (response) => {
420
+ var data = []; // create an array to save chunked data from server
421
+ // response.body is a ReadableStream
422
+ const reader = response.body.getReader();
423
+ for await (const chunk of readChunks(reader)) {
424
+ data.push(Buffer.from(chunk));
425
+ }
426
+ var buffer = Buffer.concat(data); // create buffer of data
427
+ let xmlOutput = buffer.toString("binary").trim();
428
+ let output = await parseXml(xmlOutput);
429
+ // Remove unnecessary keys
430
+ removeKeys(output, "$");
431
+
432
+ if (keyExists(output, "perfmonOpenSessionReturn")) {
433
+ var returnResults =
434
+ output.Body.perfmonOpenSessionResponse.perfmonOpenSessionReturn;
435
+
436
+ if (returnResults) {
437
+ resolve(clean(returnResults));
438
+ } else {
439
+ reject(output.Body.Fault);
440
+ }
441
+ } else {
442
+ reject({ response: "empty" });
443
+ }
444
+ })
445
+ .catch((error) => {
446
+ reject(error);
447
+ }); // catches the error and logs it
448
+ });
449
+ }
450
+ /**
451
+ * Post Fetch using Cisco PerfMon API
452
+ *
453
+ * @closeSession
454
+ var service = new perfMonService();
455
+ service.closeSession().then((success => {
456
+ console.log(success);
457
+ }))
458
+ * @memberof perfMonService
459
+ * @returns {promise} returns a Promise
460
+ */
461
+ closeSession(sessionHandle) {
462
+ var XML;
463
+ var options = this._OPTIONS;
464
+ options.SOAPAction = `perfmonCloseSession`;
465
+ var server = this._HOST;
466
+ XML = util.format(XML_CLOSE_SESSION_ENVELOPE, sessionHandle);
467
+
468
+ var soapBody = Buffer.from(XML);
469
+ options.body = soapBody;
470
+
471
+ return new Promise((resolve, reject) => {
472
+ // We fetch the API endpoint
473
+ fetch(
474
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
475
+ options
476
+ )
477
+ .then(async (response) => {
478
+ var data = []; // create an array to save chunked data from server
479
+ // response.body is a ReadableStream
480
+ const reader = response.body.getReader();
481
+ for await (const chunk of readChunks(reader)) {
482
+ data.push(Buffer.from(chunk));
483
+ }
484
+ var buffer = Buffer.concat(data); // create buffer of data
485
+ let xmlOutput = buffer.toString("binary").trim();
486
+ let output = await parseXml(xmlOutput);
487
+ // Remove unnecessary keys
488
+ removeKeys(output, "$");
489
+
490
+ if (keyExists(output, "perfmonCloseSessionResponse")) {
491
+ var returnResults = output.Body.perfmonCloseSessionResponse;
492
+ if (returnResults) {
493
+ resolve({ response: "success" });
494
+ } else {
495
+ reject(output.Body.Fault);
496
+ }
497
+ } else {
498
+ reject({ response: "empty" });
499
+ }
500
+ })
501
+ .catch((error) => {
502
+ reject(error);
503
+ }); // catches the error and logs it
504
+ });
505
+ }
506
+ /**
507
+ * Post Fetch using Cisco PerfMon API
508
+ *
509
+ * @addCounter
510
+ var service = new perfMonService();
511
+ service.addCounter().then((success => {
512
+ console.log(success);
513
+ }))
514
+ * @memberof perfMonService
515
+ * @returns {promise} returns a Promise
516
+ */
517
+ addCounter(sessionHandle, counter) {
518
+ var XML;
519
+ var counterStr;
520
+ var options = this._OPTIONS;
521
+ options.SOAPAction = `perfmonAddCounter`;
522
+ var server = this._HOST;
523
+
524
+ if (Array.isArray(counter)) {
525
+ counterStr = counter.map(
526
+ (item) =>
527
+ "<soap:Counter>" +
528
+ "<soap:Name>" +
529
+ "\\\\" +
530
+ item.host +
531
+ "\\" +
532
+ item.object +
533
+ "\\" +
534
+ item.counter +
535
+ "</soap:Name>" +
536
+ "</soap:Counter>"
537
+ );
538
+ } else {
539
+ counterStr =
540
+ "<soap:Counter>" +
541
+ "<soap:Name>" +
542
+ "\\\\" +
543
+ counter.host +
544
+ "\\" +
545
+ counter.object +
546
+ "\\" +
547
+ counter.counter +
548
+ "</soap:Name>" +
549
+ "</soap:Counter>";
550
+ }
551
+
552
+ XML = util.format(XML_ADD_COUNTER_ENVELOPE, sessionHandle, counterStr);
553
+
554
+ var soapBody = Buffer.from(XML);
555
+ options.body = soapBody;
556
+
557
+ return new Promise((resolve, reject) => {
558
+ // We fetch the API endpoint
559
+ fetch(
560
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
561
+ options
562
+ )
563
+ .then(async (response) => {
564
+ var data = []; // create an array to save chunked data from server
565
+ // response.body is a ReadableStream
566
+ const reader = response.body.getReader();
567
+ for await (const chunk of readChunks(reader)) {
568
+ data.push(Buffer.from(chunk));
569
+ }
570
+ var buffer = Buffer.concat(data); // create buffer of data
571
+ let xmlOutput = buffer.toString("binary").trim();
572
+ let output = await parseXml(xmlOutput);
573
+ // Remove unnecessary keys
574
+ removeKeys(output, "$");
575
+
576
+ if (keyExists(output, "perfmonAddCounterResponse")) {
577
+ var returnResults = output.Body.perfmonAddCounterResponse;
578
+ if (returnResults) {
579
+ resolve({ response: "success" });
580
+ } else {
581
+ reject(output.Body.Fault);
582
+ }
583
+ } else {
584
+ reject({ response: "empty" });
585
+ }
586
+ })
587
+ .catch((error) => {
588
+ reject(error);
589
+ }); // catches the error and logs it
590
+ });
591
+ }
592
+ /**
593
+ * Post Fetch using Cisco PerfMon API
594
+ *
595
+ * @removeCounter
596
+ var service = new perfMonService();
597
+ service.removeCounter().then((success => {
598
+ console.log(success);
599
+ }))
600
+ * @memberof perfMonService
601
+ * @returns {promise} returns a Promise
602
+ */
603
+ removeCounter(sessionHandle, counter) {
604
+ var XML;
605
+ var counterStr;
606
+ var options = this._OPTIONS;
607
+ options.SOAPAction = `perfmonRemoveCounter`;
608
+ var server = this._HOST;
609
+
610
+ if (Array.isArray(counter)) {
611
+ counterStr = counter.map(
612
+ (item) =>
613
+ "<soap:Counter>" +
614
+ "<soap:Name>" +
615
+ "\\\\" +
616
+ item.host +
617
+ "\\" +
618
+ item.object +
619
+ "\\" +
620
+ item.counter +
621
+ "</soap:Name>" +
622
+ "</soap:Counter>"
623
+ );
624
+ } else {
625
+ counterStr =
626
+ "<soap:Counter>" +
627
+ "<soap:Name>" +
628
+ "\\\\" +
629
+ counter.host +
630
+ "\\" +
631
+ counter.object +
632
+ "\\" +
633
+ counter.counter +
634
+ "</soap:Name>" +
635
+ "</soap:Counter>";
636
+ }
637
+
638
+ XML = util.format(XML_REMOVE_COUNTER_ENVELOPE, sessionHandle, counterStr);
639
+
640
+ var soapBody = Buffer.from(XML);
641
+ options.body = soapBody;
642
+
643
+ return new Promise((resolve, reject) => {
644
+ // We fetch the API endpoint
645
+ fetch(
646
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
647
+ options
648
+ )
649
+ .then(async (response) => {
650
+ var data = []; // create an array to save chunked data from server
651
+ // response.body is a ReadableStream
652
+ const reader = response.body.getReader();
653
+ for await (const chunk of readChunks(reader)) {
654
+ data.push(Buffer.from(chunk));
655
+ }
656
+ var buffer = Buffer.concat(data); // create buffer of data
657
+ let xmlOutput = buffer.toString("binary").trim();
658
+ let output = await parseXml(xmlOutput);
659
+ // Remove unnecessary keys
660
+ removeKeys(output, "$");
661
+
662
+ if (keyExists(output, "perfmonRemoveCounterResponse")) {
663
+ var returnResults = output.Body.perfmonRemoveCounterResponse;
664
+ if (returnResults) {
665
+ resolve({ response: "success" });
666
+ } else {
667
+ reject(output.Body.Fault);
668
+ }
669
+ } else {
670
+ reject({ response: "empty" });
671
+ }
672
+ })
673
+ .catch((error) => {
674
+ reject(error);
675
+ }); // catches the error and logs it
676
+ });
677
+ }
678
+ /**
679
+ * Post Fetch using Cisco PerfMon API
680
+ *
681
+ * @queryCounterDescription
682
+ var service = new perfMonService();
683
+ service.queryCounterDescription().then((success => {
684
+ console.log(success);
685
+ }))
686
+ * @memberof perfMonService
687
+ * @returns {promise} returns a Promise
688
+ */
689
+ queryCounterDescription(counter) {
690
+ var XML;
691
+ var options = this._OPTIONS;
692
+ options.SOAPAction = `perfmonQueryCounterDescription`;
693
+ var server = this._HOST;
694
+
695
+ var counterStr =
696
+ "<soap:Counter>" +
697
+ "\\\\" +
698
+ counter.host +
699
+ "\\" +
700
+ counter.object +
701
+ "\\" +
702
+ counter.counter +
703
+ "</soap:Counter>";
704
+
705
+ XML = util.format(XML_QUERY_COUNTER_ENVELOPE, counterStr);
706
+
707
+ var soapBody = Buffer.from(XML);
708
+ options.body = soapBody;
709
+
710
+ return new Promise((resolve, reject) => {
711
+ // We fetch the API endpoint
712
+ fetch(
713
+ `https://${server}:8443/perfmonservice2/services/PerfmonService/`,
714
+ options
715
+ )
716
+ .then(async (response) => {
717
+ var data = []; // create an array to save chunked data from server
718
+ // response.body is a ReadableStream
719
+ const reader = response.body.getReader();
720
+ for await (const chunk of readChunks(reader)) {
721
+ data.push(Buffer.from(chunk));
722
+ }
723
+ var buffer = Buffer.concat(data); // create buffer of data
724
+ let xmlOutput = buffer.toString("binary").trim();
725
+ let output = await parseXml(xmlOutput);
726
+
727
+ // Remove unnecessary keys
728
+ removeKeys(output, "$");
729
+
730
+ if (keyExists(output, "perfmonQueryCounterDescriptionReturn")) {
731
+ var returnResults =
732
+ output.Body.perfmonQueryCounterDescriptionResponse.perfmonQueryCounterDescriptionReturn;
733
+
734
+ if (returnResults) {
735
+ resolve(clean(returnResults));
736
+ } else {
737
+ reject(output.Body.Fault);
738
+ }
739
+ } else {
740
+ reject({ response: "empty" });
741
+ }
742
+ })
743
+ .catch((error) => {
744
+ reject(error);
745
+ }); // catches the error and logs it
746
+ });
747
+ }
748
+ }
749
+
750
+ // readChunks() reads from the provided reader and yields the results into an async iterable
751
+ const readChunks = (reader) => {
752
+ return {
753
+ async *[Symbol.asyncIterator]() {
754
+ let readResult = await reader.read();
755
+ while (!readResult.done) {
756
+ yield readResult.value;
757
+ readResult = await reader.read();
758
+ }
759
+ },
760
+ };
761
+ };
762
+
763
+ const keyExists = (obj, key) => {
764
+ if (!obj || (typeof obj !== "object" && !Array.isArray(obj))) {
765
+ return false;
766
+ } else if (obj.hasOwnProperty(key)) {
767
+ return true;
768
+ } else if (Array.isArray(obj)) {
769
+ for (let i = 0; i < obj.length; i++) {
770
+ const result = keyExists(obj[i], key);
771
+ if (result) {
772
+ return result;
773
+ }
774
+ }
775
+ } else {
776
+ for (const k in obj) {
777
+ const result = keyExists(obj[k], key);
778
+ if (result) {
779
+ return result;
780
+ }
781
+ }
782
+ }
783
+
784
+ return false;
785
+ };
786
+
787
+ /**
788
+ * Remove all specified keys from an object, no matter how deep they are.
789
+ * The removal is done in place, so run it on a copy if you don't want to modify the original object.
790
+ * This function has no limit so circular objects will probably crash the browser
791
+ *
792
+ * @param obj The object from where you want to remove the keys
793
+ * @param keys An array of property names (strings) to remove
794
+ */
795
+ const removeKeys = (obj, keys) => {
796
+ for (var prop in obj) {
797
+ if (obj.hasOwnProperty(prop)) {
798
+ switch (typeof obj[prop]) {
799
+ case "object":
800
+ if (keys.indexOf(prop) > -1) {
801
+ delete obj[prop];
802
+ } else {
803
+ removeKeys(obj[prop], keys);
804
+ }
805
+ break;
806
+ default:
807
+ if (keys.indexOf(prop) > -1) {
808
+ delete obj[prop];
809
+ }
810
+ break;
811
+ }
812
+ }
813
+ }
814
+ };
815
+
816
+ const clean = (object) => {
817
+ Object.entries(object).forEach(([k, v]) => {
818
+ if (v && typeof v === "object") {
819
+ clean(v);
820
+ }
821
+ if (
822
+ (v && typeof v === "object" && !Object.keys(v).length) ||
823
+ v === null ||
824
+ v === undefined
825
+ ) {
826
+ if (Array.isArray(object)) {
827
+ object.splice(k, 1);
828
+ } else {
829
+ delete object[k];
830
+ }
831
+ }
832
+ });
833
+ return object;
834
+ };
835
+
836
+ const parseXml = (xmlPart) => {
837
+ return new Promise((resolve, reject) => {
838
+ parseString(
839
+ xmlPart,
840
+ {
841
+ explicitArray: false,
842
+ explicitRoot: false,
843
+ tagNameProcessors: [stripPrefix],
844
+ },
845
+ (err, result) => {
846
+ if (err) {
847
+ reject(err);
848
+ } else {
849
+ resolve(result);
850
+ }
851
+ }
852
+ );
853
+ });
854
+ };
855
+
856
+ module.exports = perfMonService;
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "cisco-perfmon",
3
+ "version": "1.1.0",
4
+ "description": "A library to pull Perfmon data from Cisco VOS applications via SOAP",
5
+ "main": "main.js",
6
+ "scripts": {
7
+ "test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 NODE_TLS_REJECT_UNAUTHORIZED=0 node ./test/tests.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/sieteunoseis/cisco-perfmon.git"
12
+ },
13
+ "keywords": [
14
+ "cisco",
15
+ "node",
16
+ "perfmon",
17
+ "cucm",
18
+ "vos"
19
+ ],
20
+ "author": "Jeremy Worden <jeremy@automate.builders> (https://automate.builders/)",
21
+ "license": "MIT",
22
+ "bugs": {
23
+ "url": "https://github.com/sieteunoseis/cisco-perfmon/issues"
24
+ },
25
+ "homepage": "https://github.com/sieteunoseis/cisco-perfmon#readme",
26
+ "dependencies": {
27
+ "util": "*",
28
+ "xml2js": "*"
29
+ }
30
+ }
package/test/tests.js ADDED
@@ -0,0 +1,108 @@
1
+ const perfMonService = require("../main");
2
+
3
+ // Set up new PerfMon service
4
+ let service = new perfMonService(
5
+ "10.10.20.1",
6
+ "administrator",
7
+ "ciscopsdt"
8
+ );
9
+
10
+ // Variables to hold our SessionID and our Session Counter
11
+ var SessionID;
12
+ var counterObj = {
13
+ host: "cucm01-pub",
14
+ object: "Cisco CallManager",
15
+ counter: "CallsActive",
16
+ };
17
+
18
+ console.log("Let's get a description of our counter.");
19
+ service
20
+ .queryCounterDescription(counterObj)
21
+ .then((results) => {
22
+ console.log("queryCounterDescription", results);
23
+ })
24
+ .catch((error) => {
25
+ console.log(error);
26
+ });
27
+
28
+ console.log(
29
+ "Let's open a session, add a counter, wait 30 seconds, collect the session data, remove the counter and finally close the session"
30
+ );
31
+ service
32
+ .openSession()
33
+ .then((results) => {
34
+ console.log("SessionID", results);
35
+ SessionID = results;
36
+ service
37
+ .addCounter(SessionID, counterObj)
38
+ .then(async (results) => {
39
+ console.log("addCounter", results);
40
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
41
+ console.log("Wait 30 seconds");
42
+ await delay(30000); /// waiting 30 second.
43
+ service
44
+ .collectSessionData(SessionID)
45
+ .then((results) => {
46
+ console.log("collectSessionData", results);
47
+ service
48
+ .removeCounter(SessionID, counterObj)
49
+ .then((results) => {
50
+ console.log("removeCounter", results);
51
+ service
52
+ .closeSession(SessionID)
53
+ .then((results) => {
54
+ console.log("closeSession", results);
55
+ })
56
+ .catch((error) => {
57
+ console.log(error);
58
+ });
59
+ })
60
+ .catch((error) => {
61
+ console.log(error);
62
+ });
63
+ })
64
+ .catch((error) => {
65
+ console.log(error);
66
+ });
67
+ })
68
+ .catch((error) => {
69
+ console.log(error);
70
+ });
71
+ })
72
+ .catch((error) => {
73
+ console.log(error);
74
+ });
75
+
76
+ console.log("Let's collect some non session counter data.");
77
+ service
78
+ .collectCounterData("cucm01-pub", "Cisco CallManager")
79
+ .then((results) => {
80
+ console.log("collectCounterData", results);
81
+ })
82
+ .catch((error) => {
83
+ console.log(error);
84
+ });
85
+
86
+ console.log(
87
+ "Let's returns the list of available PerfMon objects and counters on a particular host"
88
+ );
89
+ service
90
+ .listCounter("cucm01-pub")
91
+ .then((results) => {
92
+ console.log("listCounter", results);
93
+ })
94
+ .catch((error) => {
95
+ console.log(error);
96
+ });
97
+
98
+ console.log(
99
+ "Let's return a list of instances of a PerfMon object on a particular host. Instances of an object can dynamically change. This operation returns the most recent list."
100
+ );
101
+ service
102
+ .listInstance("cucm01-pub","Partition")
103
+ .then((results) => {
104
+ console.log("listInstance", results);
105
+ })
106
+ .catch((error) => {
107
+ console.log(error);
108
+ });