cisco-perfmon 1.6.1 → 2.0.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.
@@ -10,7 +10,22 @@ permissions:
10
10
  contents: write
11
11
 
12
12
  jobs:
13
+ test:
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
+
24
+ - name: Install dependencies
25
+ run: npm ci
26
+
13
27
  publish:
28
+ needs: test
14
29
  runs-on: ubuntu-latest
15
30
  steps:
16
31
  - name: Checkout
package/README.md CHANGED
@@ -1,92 +1,393 @@
1
- # Cisco RisPort Library
1
+ # cisco-perfmon
2
2
 
3
- Simple library to pull Perfmon stats from a Cisco CUCM via SOAP.
3
+ A library and CLI for collecting real-time performance counters from Cisco CUCM via the PerfMon SOAP API.
4
4
 
5
- Perfmon information can be found at
6
- [PerfMon API Reference](https://developer.cisco.com/docs/sxml/#!perfmon-api-reference).
5
+ [![npm](https://img.shields.io/npm/v/cisco-perfmon)](https://www.npmjs.com/package/cisco-perfmon)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![skills.sh](https://img.shields.io/badge/skills.sh-cisco--perfmon--cli-blue)](https://skills.sh/skills/cisco-perfmon-cli)
8
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?logo=buymeacoffee)](https://buymeacoffee.com/automatebldrs)
9
+
10
+ Perfmon API reference: [Cisco PerfMon API Reference](https://developer.cisco.com/docs/sxml/#!perfmon-api-reference)
7
11
 
8
12
  ## Installation
9
13
 
10
- Using npm:
14
+ ### As a library
11
15
 
12
- ```javascript
13
- npm i -g npm
16
+ ```bash
14
17
  npm i --save cisco-perfmon
15
18
  ```
16
19
 
17
- ## Requirements
20
+ ### As a CLI
18
21
 
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.
22
+ ```bash
23
+ npm install -g cisco-perfmon
24
+ ```
20
25
 
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.
26
+ ## Requirements
22
27
 
23
- Suggested enviromental variables:
28
+ Node.js 18+ is required (uses the built-in Fetch API).
29
+
30
+ If you are using self-signed certificates on Cisco VOS products you may need to disable TLS verification. **Only do this in a lab environment.**
24
31
 
25
32
  ```env
26
- NODE_OPTIONS=--experimental-vm-modules
27
- NODE_NO_WARNINGS=1
28
33
  NODE_TLS_REJECT_UNAUTHORIZED=0
29
34
  ```
30
35
 
31
- ## Usage
36
+ ## CLI Quick Start
37
+
38
+ ```bash
39
+ # Configure a cluster
40
+ cisco-perfmon config add lab --host cucm-pub.example.com --username admin --password secret --insecure
41
+
42
+ # Verify connectivity
43
+ cisco-perfmon doctor
44
+
45
+ # List available counter objects
46
+ cisco-perfmon list-objects
47
+
48
+ # Collect CallManager counters
49
+ cisco-perfmon collect "Cisco CallManager"
50
+
51
+ # Watch counters live with sparklines
52
+ cisco-perfmon watch "Cisco CallManager" --counter CallsActive,CallsInProgress --interval 5
53
+ ```
54
+
55
+ ## CLI Commands
56
+
57
+ ### config -- Manage cluster configurations
58
+
59
+ ```bash
60
+ cisco-perfmon config add <name> --host <host> --username <user> --password <pass>
61
+ cisco-perfmon config use <name> # switch active cluster
62
+ cisco-perfmon config list # list all clusters
63
+ cisco-perfmon config show # show active cluster details
64
+ cisco-perfmon config remove <name> # remove a cluster
65
+ cisco-perfmon config test # test connectivity
66
+ ```
67
+
68
+ Supports Secret Server integration for credential management:
32
69
 
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:
70
+ ```bash
71
+ cisco-perfmon config add <name> --host '<ss:ID:host>' --username '<ss:ID:username>' --password '<ss:ID:password>'
72
+ ```
73
+
74
+ Or use environment variables instead of config files:
75
+
76
+ ```bash
77
+ export CUCM_HOST=cucm-pub.example.com
78
+ export CUCM_USERNAME=admin
79
+ export CUCM_PASSWORD=secret
80
+ ```
81
+
82
+ Connection precedence: CLI flags > environment variables > config file.
83
+
84
+ ### list-objects -- List available perfmon counter objects
85
+
86
+ ```bash
87
+ cisco-perfmon list-objects # all objects
88
+ cisco-perfmon list-objects --search "CallManager" # filter by keyword
89
+ ```
90
+
91
+ ### list-instances -- List instances of a perfmon object
92
+
93
+ ```bash
94
+ cisco-perfmon list-instances "Cisco CallManager"
95
+ cisco-perfmon list-instances "Process" --format json
96
+ ```
97
+
98
+ ### describe -- Get counter descriptions
99
+
100
+ ```bash
101
+ cisco-perfmon describe "Cisco CallManager"
102
+ cisco-perfmon describe "Cisco CallManager" --counter CallsActive
103
+ ```
104
+
105
+ ### collect -- One-shot counter data collection
106
+
107
+ ```bash
108
+ cisco-perfmon collect "Cisco CallManager" # all counters
109
+ cisco-perfmon collect "Cisco CallManager" --counter CallsActive,CallsInProgress # specific counters
110
+ cisco-perfmon collect "Cisco CallManager" --instance "" # filter by instance
111
+ cisco-perfmon collect "Memory" --format csv > memory.csv # export to CSV
112
+ ```
113
+
114
+ ### session -- Manage perfmon polling sessions
115
+
116
+ For fine-grained control over which counters to poll:
117
+
118
+ ```bash
119
+ cisco-perfmon session open # get a session handle
120
+ cisco-perfmon session add <handle> --counters '[{"host":"cucm","object":"Cisco CallManager","counter":"CallsActive"}]'
121
+ cisco-perfmon session collect <handle> # collect session data
122
+ cisco-perfmon session remove <handle> --counters '[...]' # remove counters
123
+ cisco-perfmon session close <handle> # close session
124
+ ```
125
+
126
+ ### watch -- Continuous monitoring with live sparklines
127
+
128
+ The `watch` command is the standout feature for real-time monitoring. It polls counters at a configurable interval and renders a live-updating table with sparkline visualizations showing trends over the last 12 samples.
129
+
130
+ ```bash
131
+ cisco-perfmon watch "Cisco CallManager" # watch all counters
132
+ cisco-perfmon watch "Cisco CallManager" --counter CallsActive --interval 5 # 5-second polling
133
+ cisco-perfmon watch "Processor" --counter "% CPU Time" --instance "_Total" # CPU monitoring
134
+ cisco-perfmon watch "Memory" --interval 30 --duration 300 # 5-minute memory check
135
+ ```
136
+
137
+ The table view displays:
138
+
139
+ | Column | Description |
140
+ |-----------|------------------------------------|
141
+ | counter | Counter name |
142
+ | instance | Instance name |
143
+ | value | Current value |
144
+ | sparkline | Visual trend (last 12 samples) |
145
+ | min | Minimum observed value |
146
+ | max | Maximum observed value |
147
+ | avg | Average across samples |
148
+
149
+ Press `Ctrl+C` to stop. A final summary shows iteration count and total duration.
150
+
151
+ ### doctor -- Configuration and connectivity health check
152
+
153
+ ```bash
154
+ cisco-perfmon doctor
155
+ cisco-perfmon doctor --insecure
156
+ ```
157
+
158
+ Runs checks against: active cluster config, PerfMon API connectivity, counter object availability, config file permissions, and audit trail size.
159
+
160
+ ## Output Formats
161
+
162
+ All commands support four output formats via the `--format` flag:
163
+
164
+ - `--format table` (default) -- human-readable table
165
+ - `--format json` -- structured JSON for scripting
166
+ - `--format toon` -- token-efficient format for AI agents
167
+ - `--format csv` -- comma-separated values for spreadsheets
168
+
169
+ ## Global Flags
170
+
171
+ | Flag | Description |
172
+ |---------------------|------------------------------------------------|
173
+ | `--host <host>` | Override CUCM hostname |
174
+ | `--username <user>` | Override CUCM username |
175
+ | `--password <pass>` | Override CUCM password |
176
+ | `--cluster <name>` | Use a specific named cluster |
177
+ | `--format <type>` | Output format: table, json, toon, csv |
178
+ | `--insecure` | Skip TLS certificate verification |
179
+ | `--no-audit` | Disable audit logging for this command |
180
+ | `--debug` | Enable debug logging |
181
+
182
+ ## Library Usage
183
+
184
+ ### CommonJS
185
+
186
+ ```javascript
187
+ const perfMonService = require("cisco-perfmon");
188
+ ```
189
+
190
+ ### ESM
191
+
192
+ ```javascript
193
+ import perfMonService from "cisco-perfmon";
194
+ ```
195
+
196
+ ### Basic example
34
197
 
35
198
  ```javascript
36
- const perfMonService = require("../main");
199
+ const perfMonService = require("cisco-perfmon");
37
200
 
38
- // Set up new PerfMon service
39
- let service = new perfMonService(
40
- "10.10.20.1",
41
- "administrator",
42
- "ciscopsdt"
43
- );
201
+ const service = new perfMonService("10.10.20.1", "administrator", "ciscopsdt");
44
202
 
45
- var counterObj = {
46
- host: cucmServerName,
203
+ const counter = {
204
+ host: "cucm01-pub",
47
205
  object: "Cisco CallManager",
48
206
  instance: "",
49
207
  counter: "CallsActive",
50
208
  };
51
209
 
52
- console.log("Let's get a description of our counter.");
53
- service
54
- .queryCounterDescription(counterObj)
55
- .then((response) => {
56
- console.log("queryCounterDescription", response.results);
57
- })
58
- .catch((error) => {
59
- console.log(error.message);
60
- });
210
+ const result = await service.collectCounterData(counter.host, counter.object);
211
+ console.log(result.results);
61
212
  ```
62
213
 
63
- ## Examples
214
+ ## Constructor
64
215
 
65
216
  ```javascript
66
- npm run test
217
+ new perfMonService(host, username, password, options, retry)
218
+ ```
219
+
220
+ | Parameter | Type | Default | Description |
221
+ |------------|---------|---------|--------------------------------------------------|
222
+ | `host` | string | -- | CUCM IP or FQDN |
223
+ | `username` | string | -- | AXL/admin username (omit if using SSO cookie) |
224
+ | `password` | string | -- | Password (omit if using SSO cookie) |
225
+ | `options` | object | `{}` | See options below |
226
+ | `retry` | boolean | `true` | Enable/disable automatic retry |
227
+
228
+ ### Options
229
+
230
+ | Option | Type | Default | Description |
231
+ |--------------|--------|---------|-----------------------------------------------------------------------|
232
+ | `retries` | number | `3` | Max retry attempts. Falls back to `PM_RETRY` env var. |
233
+ | `retryDelay` | number | `5000` | Delay in ms between retries. Falls back to `PM_RETRY_DELAY` env var. |
234
+ | `Cookie` | string | -- | Session cookie for SSO authentication |
235
+ | _any header_ | string | -- | Additional HTTP headers merged into every request |
236
+
237
+ ```javascript
238
+ // Custom retry settings
239
+ const service = new perfMonService("10.10.20.1", "admin", "pass", {
240
+ retries: 5,
241
+ retryDelay: 2000,
242
+ });
243
+
244
+ // SSO cookie auth (no username/password needed)
245
+ const service = new perfMonService("10.10.20.1", "", "", {
246
+ Cookie: "JSESSIONIDSSO=abc123",
247
+ });
248
+ ```
249
+
250
+ ## Rate Limiting
251
+
252
+ CUCM enforces an 80 requests/minute limit on Perfmon. This library automatically detects that SOAP fault and applies exponential backoff (30s -> 60s -> 120s) before retrying.
253
+
254
+ ## Cookie Management
255
+
256
+ Cookies returned by CUCM are automatically captured and reused for subsequent requests.
257
+
258
+ ```javascript
259
+ // Get the current stored cookie
260
+ const cookie = service.getCookie();
261
+
262
+ // Set a cookie manually (e.g. from a prior session)
263
+ service.setCookie("JSESSIONIDSSO=abc123");
264
+ ```
265
+
266
+ ## Methods
267
+
268
+ ### `collectCounterData(host, object)`
269
+
270
+ Collect counter data without a session.
271
+
272
+ ```javascript
273
+ const result = await service.collectCounterData("cucm01-pub", "Cisco CallManager");
274
+ // result.results => [{ host, object, instance, counter, value, cstatus }, ...]
275
+ ```
276
+
277
+ ### `collectSessionData(sessionHandle)`
278
+
279
+ Collect data for an open session.
280
+
281
+ ```javascript
282
+ const result = await service.collectSessionData(sessionHandle);
283
+ ```
284
+
285
+ ### `listCounter(host, filtered?)`
286
+
287
+ List all available counters on a host, with optional name filtering.
288
+
289
+ ```javascript
290
+ const result = await service.listCounter("cucm01-pub");
291
+ const filtered = await service.listCounter("cucm01-pub", ["Cisco CallManager", "Memory"]);
292
+ ```
293
+
294
+ ### `listInstance(host, object)`
295
+
296
+ List instances of a perfmon object.
297
+
298
+ ```javascript
299
+ const result = await service.listInstance("cucm01-pub", "Cisco CallManager");
300
+ ```
301
+
302
+ ### `openSession()` / `closeSession(sessionHandle)`
303
+
304
+ Open and close a polling session.
305
+
306
+ ```javascript
307
+ const opened = await service.openSession();
308
+ const sessionHandle = opened.results;
309
+ // ... collect data ...
310
+ await service.closeSession(sessionHandle);
311
+ ```
312
+
313
+ ### `addCounter(sessionHandle, counter)` / `removeCounter(sessionHandle, counter)`
314
+
315
+ Add or remove counters from an open session. Accepts a single counter object or an array.
316
+
317
+ ```javascript
318
+ await service.addCounter(sessionHandle, {
319
+ host: "cucm01-pub",
320
+ object: "Cisco CallManager",
321
+ instance: "",
322
+ counter: "CallsActive",
323
+ });
324
+ ```
325
+
326
+ ### `queryCounterDescription(counter)`
327
+
328
+ Get the description of a counter.
329
+
330
+ ```javascript
331
+ const result = await service.queryCounterDescription({
332
+ host: "cucm01-pub",
333
+ object: "Cisco CallManager",
334
+ instance: "",
335
+ counter: "CallsActive",
336
+ });
67
337
  ```
68
338
 
69
339
  ## Output Examples
70
340
 
341
+ ```javascript
342
+ // Success
343
+ {
344
+ host: 'cucm01-pub',
345
+ object: 'Cisco CallManager',
346
+ instance: '',
347
+ counter: 'PRIChannelsActive',
348
+ value: '0',
349
+ cstatus: '1'
350
+ }
351
+
352
+ // Rate limit error (automatically retried with backoff)
353
+ {
354
+ status: 500,
355
+ code: 'Internal Server Error',
356
+ host: 'cucm01-pub',
357
+ message: 'Exceeded allowed rate for Perfmon information. Current allowed rate for perfmon information is 80 requests per minute.'
358
+ }
71
359
  ```
72
- Success Example
73
- {
74
- host: 'cucm01-pub',
75
- object: 'Cisco CallManager',
76
- instance: '',
77
- counter: 'PRIChannelsActive',
78
- value: '0',
79
- cstatus: '1'
80
- }
81
360
 
82
- Error Example
83
- {
84
- status: 500,
85
- code: 'Internal Server Error',
86
- host: 'cucm01-pub',
87
- counter: 'SAML SSO',
88
- message: 'Exceeded allowed rate for Perfmon information. Current allowed rate for perfmon information is 80 requests per minute.PerfmonService'
89
- }
361
+ ## Environment Variables
362
+
363
+ These are supported as fallbacks when constructor options are not provided:
364
+
365
+ | Variable | Description | Default |
366
+ |-------------------|--------------------------------------|---------|
367
+ | `PM_RETRY` | Max retry attempts | `3` |
368
+ | `PM_RETRY_DELAY` | Delay in ms between retries | `5000` |
369
+
370
+ ## Related Tools
371
+
372
+ | Package | Description |
373
+ |---------|-------------|
374
+ | [cisco-axl](https://www.npmjs.com/package/cisco-axl) | Cisco CUCM AXL API library and CLI |
375
+ | [cisco-risport](https://www.npmjs.com/package/cisco-risport) | Cisco CUCM RisPort70 real-time device status |
376
+ | [cisco-ise](https://www.npmjs.com/package/cisco-ise) | Cisco ISE ERS API library and CLI |
377
+
378
+ ## Testing
379
+
380
+ ```bash
381
+ npm test # run unit tests
382
+ npm run test:integration # run integration tests (requires CUCM)
90
383
  ```
91
384
 
92
- Note: Test are using Cisco's DevNet sandbox information. Find more information here: [Cisco DevNet](https://devnetsandbox.cisco.com/)
385
+ ## Funding
386
+
387
+ If you find this tool useful, consider supporting development:
388
+
389
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?logo=buymeacoffee&style=for-the-badge)](https://buymeacoffee.com/automatebldrs)
390
+
391
+ ## License
392
+
393
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require("../cli/index.js");
@@ -0,0 +1,48 @@
1
+ const { resolveConfig } = require("../utils/connection.js");
2
+ const { printResult, printError } = require("../utils/output.js");
3
+ const audit = require("../utils/audit.js");
4
+
5
+ module.exports = function (program) {
6
+ program
7
+ .command("collect <object>")
8
+ .description("Collect perfmon counter data (one-shot)")
9
+ .option("--counter <names>", "comma-separated counter names to filter")
10
+ .option("--instance <name>", "filter by instance name")
11
+ .action(async (object, opts, cmd) => {
12
+ const globalOpts = cmd.optsWithGlobals();
13
+ const start = Date.now();
14
+ try {
15
+ const connConfig = resolveConfig(globalOpts);
16
+ if (connConfig.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
17
+ if (globalOpts.debug) process.env.DEBUG = "cisco-perfmon";
18
+
19
+ const PerfMon = require("../../main.js");
20
+ const svc = new PerfMon(connConfig.host, connConfig.username, connConfig.password);
21
+ const result = await svc.collectCounterData(connConfig.host, object);
22
+
23
+ let rows = Array.isArray(result.results) ? result.results : result.results ? [result.results] : [];
24
+
25
+ // Filter by counter names if specified
26
+ if (opts.counter) {
27
+ const counterNames = opts.counter.split(",").map((c) => c.trim());
28
+ rows = rows.filter((r) => counterNames.includes(r.counter));
29
+ }
30
+
31
+ // Filter by instance if specified
32
+ if (opts.instance) {
33
+ rows = rows.filter((r) => r.instance === opts.instance);
34
+ }
35
+
36
+ if (globalOpts.audit !== false) {
37
+ audit.log({ cluster: connConfig.host, command: "collect", args: object, duration_ms: Date.now() - start, status: "success", rows: rows.length });
38
+ }
39
+
40
+ await printResult(rows, globalOpts.format);
41
+ } catch (err) {
42
+ if (globalOpts.audit !== false) {
43
+ try { audit.log({ cluster: "", command: "collect", args: object, duration_ms: Date.now() - start, status: "error", message: err.message }); } catch {}
44
+ }
45
+ printError(err);
46
+ }
47
+ });
48
+ };
@@ -0,0 +1,85 @@
1
+ const configUtil = require("../utils/config.js");
2
+ const { printResult, printError } = require("../utils/output.js");
3
+
4
+ module.exports = function (program) {
5
+ const config = program.command("config").description("Manage CUCM cluster configurations");
6
+
7
+ config
8
+ .command("add <name>")
9
+ .description("Add a CUCM cluster (requires global --host, --username, --password)")
10
+ .option("--insecure", "skip TLS verification for this cluster")
11
+ .action((name, opts, cmd) => {
12
+ try {
13
+ const globalOpts = cmd.optsWithGlobals();
14
+ const host = globalOpts.host;
15
+ const username = globalOpts.username;
16
+ const password = globalOpts.password;
17
+ if (!host) throw new Error("Missing required option: --host");
18
+ if (!username) throw new Error("Missing required option: --username");
19
+ if (!password) throw new Error("Missing required option: --password");
20
+ const clusterOpts = { host, username, password };
21
+ if (opts.insecure || globalOpts.insecure) clusterOpts.insecure = true;
22
+ configUtil.addCluster(name, clusterOpts);
23
+ console.log(`Cluster "${name}" added successfully.`);
24
+ } catch (err) { printError(err); }
25
+ });
26
+
27
+ config
28
+ .command("use <name>")
29
+ .description("Set the active cluster")
30
+ .action((name) => {
31
+ try { configUtil.useCluster(name); console.log(`Active cluster set to "${name}".`); }
32
+ catch (err) { printError(err); }
33
+ });
34
+
35
+ config
36
+ .command("list")
37
+ .description("List all configured clusters")
38
+ .action(async () => {
39
+ try {
40
+ const { activeCluster, clusters } = configUtil.listClusters();
41
+ const rows = Object.entries(clusters).map(([name, c]) => ({
42
+ name, active: name === activeCluster ? "*" : "", host: c.host, username: c.username,
43
+ }));
44
+ if (rows.length === 0) { console.log("No clusters configured. Run: cisco-perfmon config add <name> ..."); return; }
45
+ await printResult(rows, program.opts().format);
46
+ } catch (err) { printError(err); }
47
+ });
48
+
49
+ config
50
+ .command("show")
51
+ .description("Show active cluster details (masks password)")
52
+ .action(async () => {
53
+ try {
54
+ const cluster = configUtil.getActiveCluster(program.opts().cluster);
55
+ if (!cluster) { console.log("No active cluster. Run: cisco-perfmon config add <name> ..."); return; }
56
+ await printResult({ ...cluster, password: configUtil.maskPassword(cluster.password) }, program.opts().format);
57
+ } catch (err) { printError(err); }
58
+ });
59
+
60
+ config
61
+ .command("remove <name>")
62
+ .description("Remove a cluster")
63
+ .action((name) => {
64
+ try { configUtil.removeCluster(name); console.log(`Cluster "${name}" removed.`); }
65
+ catch (err) { printError(err); }
66
+ });
67
+
68
+ config
69
+ .command("test")
70
+ .description("Test connection to the active cluster")
71
+ .action(async (opts, cmd) => {
72
+ try {
73
+ const globalOpts = cmd.optsWithGlobals();
74
+ const { resolveConfig } = require("../utils/connection.js");
75
+ const connConfig = resolveConfig(globalOpts);
76
+ if (connConfig.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
77
+ const PerfMon = require("../../main.js");
78
+ const svc = new PerfMon(connConfig.host, connConfig.username, connConfig.password);
79
+ const result = await svc.listCounter(connConfig.host);
80
+ const count = Array.isArray(result.results) ? result.results.length : 0;
81
+ console.log(`Connection validated. PerfMon at ${connConfig.host}:8443 is reachable.`);
82
+ console.log(`Counter objects available: ${count}`);
83
+ } catch (err) { printError(err); }
84
+ });
85
+ };
@@ -0,0 +1,43 @@
1
+ const { resolveConfig } = require("../utils/connection.js");
2
+ const { printResult, printError } = require("../utils/output.js");
3
+ const audit = require("../utils/audit.js");
4
+
5
+ module.exports = function (program) {
6
+ program
7
+ .command("describe <object>")
8
+ .description("Describe a perfmon counter")
9
+ .option("--counter <name>", "counter name to describe")
10
+ .option("--instance <name>", "instance name")
11
+ .action(async (object, opts, cmd) => {
12
+ const globalOpts = cmd.optsWithGlobals();
13
+ const start = Date.now();
14
+ try {
15
+ const connConfig = resolveConfig(globalOpts);
16
+ if (connConfig.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
17
+ if (globalOpts.debug) process.env.DEBUG = "cisco-perfmon";
18
+
19
+ const PerfMon = require("../../main.js");
20
+ const svc = new PerfMon(connConfig.host, connConfig.username, connConfig.password);
21
+
22
+ const counterObj = {
23
+ host: connConfig.host,
24
+ object: object,
25
+ };
26
+ if (opts.counter) counterObj.counter = opts.counter;
27
+ if (opts.instance) counterObj.instance = opts.instance;
28
+
29
+ const result = await svc.queryCounterDescription(counterObj);
30
+
31
+ if (globalOpts.audit !== false) {
32
+ audit.log({ cluster: connConfig.host, command: "describe", args: `${object} ${opts.counter || ""}`.trim(), duration_ms: Date.now() - start, status: "success" });
33
+ }
34
+
35
+ await printResult(result.results || result, globalOpts.format);
36
+ } catch (err) {
37
+ if (globalOpts.audit !== false) {
38
+ try { audit.log({ cluster: "", command: "describe", args: object, duration_ms: Date.now() - start, status: "error", message: err.message }); } catch {}
39
+ }
40
+ printError(err);
41
+ }
42
+ });
43
+ };