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.
- package/.github/workflows/release.yml +15 -0
- package/README.md +354 -53
- package/bin/cisco-perfmon.js +2 -0
- package/cli/commands/collect.js +48 -0
- package/cli/commands/config.js +85 -0
- package/cli/commands/describe.js +43 -0
- package/cli/commands/doctor.js +115 -0
- package/cli/commands/list-instances.js +39 -0
- package/cli/commands/list-objects.js +45 -0
- package/cli/commands/session.js +162 -0
- package/cli/commands/watch.js +163 -0
- package/cli/formatters/csv.js +10 -0
- package/cli/formatters/json.js +5 -0
- package/cli/formatters/table.js +25 -0
- package/cli/formatters/toon.js +6 -0
- package/cli/index.js +40 -0
- package/cli/utils/audit.js +30 -0
- package/cli/utils/config.js +112 -0
- package/cli/utils/connection.js +36 -0
- package/cli/utils/output.js +28 -0
- package/package.json +13 -2
- package/skills/cisco-perfmon-cli/SKILL.md +176 -0
- package/test/cli/config.test.js +196 -0
- package/test/cli/connection.test.js +151 -0
- package/test/cli/formatters.test.js +144 -0
- package/test/cli/watch.test.js +76 -0
- package/test/unit.js +24 -0
|
@@ -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
|
-
#
|
|
1
|
+
# cisco-perfmon
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A library and CLI for collecting real-time performance counters from Cisco CUCM via the PerfMon SOAP API.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[
|
|
5
|
+
[](https://www.npmjs.com/package/cisco-perfmon)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://skills.sh/skills/cisco-perfmon-cli)
|
|
8
|
+
[](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
|
-
|
|
14
|
+
### As a library
|
|
11
15
|
|
|
12
|
-
```
|
|
13
|
-
npm i -g npm
|
|
16
|
+
```bash
|
|
14
17
|
npm i --save cisco-perfmon
|
|
15
18
|
```
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
### As a CLI
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g cisco-perfmon
|
|
24
|
+
```
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
## Requirements
|
|
22
27
|
|
|
23
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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("
|
|
199
|
+
const perfMonService = require("cisco-perfmon");
|
|
37
200
|
|
|
38
|
-
|
|
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
|
-
|
|
46
|
-
host:
|
|
203
|
+
const counter = {
|
|
204
|
+
host: "cucm01-pub",
|
|
47
205
|
object: "Cisco CallManager",
|
|
48
206
|
instance: "",
|
|
49
207
|
counter: "CallsActive",
|
|
50
208
|
};
|
|
51
209
|
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
##
|
|
214
|
+
## Constructor
|
|
64
215
|
|
|
65
216
|
```javascript
|
|
66
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
385
|
+
## Funding
|
|
386
|
+
|
|
387
|
+
If you find this tool useful, consider supporting development:
|
|
388
|
+
|
|
389
|
+
[](https://buymeacoffee.com/automatebldrs)
|
|
390
|
+
|
|
391
|
+
## License
|
|
392
|
+
|
|
393
|
+
MIT
|
|
@@ -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
|
+
};
|