cisco-ise 1.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/EXAMPLES.md +46 -0
- package/RADIUS.md +19 -0
- package/README.md +222 -0
- package/bin/cisco-ise.js +14 -0
- package/cli/commands/auth-profile.js +36 -0
- package/cli/commands/config.js +140 -0
- package/cli/commands/deployment.js +49 -0
- package/cli/commands/endpoint.js +167 -0
- package/cli/commands/guest.js +220 -0
- package/cli/commands/identity-group.js +24 -0
- package/cli/commands/internal-user.js +162 -0
- package/cli/commands/network-device.js +167 -0
- package/cli/commands/radius.js +326 -0
- package/cli/commands/session.js +123 -0
- package/cli/commands/tacacs.js +125 -0
- package/cli/commands/trustsec.js +37 -0
- package/cli/formatters/csv.js +10 -0
- package/cli/formatters/json.js +5 -0
- package/cli/formatters/table.js +29 -0
- package/cli/formatters/toon.js +6 -0
- package/cli/index.js +44 -0
- package/cli/utils/api.js +297 -0
- package/cli/utils/audit.js +30 -0
- package/cli/utils/config.js +125 -0
- package/cli/utils/confirm.js +34 -0
- package/cli/utils/connection.js +47 -0
- package/cli/utils/failure-reasons.js +2086 -0
- package/cli/utils/mac.js +18 -0
- package/cli/utils/output.js +42 -0
- package/cli/utils/spinner.js +19 -0
- package/cli/utils/time.js +21 -0
- package/cli/utils/wordlist.js +9 -0
- package/docs/PHASES.md +38 -0
- package/package.json +45 -0
- package/skills/cisco-ise-cli/SKILL.md +346 -0
- package/test/cli/api.test.js +67 -0
- package/test/cli/audit.test.js +31 -0
- package/test/cli/config.test.js +60 -0
- package/test/cli/confirm.test.js +34 -0
- package/test/cli/connection.test.js +54 -0
- package/test/cli/formatters.test.js +41 -0
- package/test/cli/mac.test.js +37 -0
- package/test/cli/time.test.js +30 -0
- package/test/integration/ise.test.js +425 -0
package/EXAMPLES.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
cisco-ise config add lab --host ise01.automate.builders --username admin --password '<your-pass>' --insecure
|
|
3
|
+
cisco-ise config test
|
|
4
|
+
|
|
5
|
+
# Endpoints
|
|
6
|
+
cisco-ise endpoint list
|
|
7
|
+
cisco-ise endpoint list --format json --limit 10
|
|
8
|
+
cisco-ise endpoint search --mac E2:7C:7E:5B:F0:E0
|
|
9
|
+
|
|
10
|
+
# Identity groups & auth profiles
|
|
11
|
+
cisco-ise identity-group list
|
|
12
|
+
cisco-ise identity-group list --type endpoint
|
|
13
|
+
cisco-ise auth-profile list
|
|
14
|
+
|
|
15
|
+
# Network devices
|
|
16
|
+
cisco-ise network-device list
|
|
17
|
+
|
|
18
|
+
# Deployment
|
|
19
|
+
cisco-ise deployment nodes
|
|
20
|
+
cisco-ise deployment status
|
|
21
|
+
|
|
22
|
+
# Sessions
|
|
23
|
+
cisco-ise session list
|
|
24
|
+
|
|
25
|
+
# RADIUS
|
|
26
|
+
cisco-ise radius failures --last 1h
|
|
27
|
+
|
|
28
|
+
# TACACS
|
|
29
|
+
cisco-ise tacacs command-sets
|
|
30
|
+
cisco-ise tacacs profiles
|
|
31
|
+
|
|
32
|
+
# TrustSec
|
|
33
|
+
cisco-ise trustsec sgt list
|
|
34
|
+
cisco-ise trustsec sgacl list
|
|
35
|
+
|
|
36
|
+
# Guest
|
|
37
|
+
cisco-ise guest portals
|
|
38
|
+
cisco-ise guest list
|
|
39
|
+
|
|
40
|
+
# Dry run a write operation (safe — no changes)
|
|
41
|
+
cisco-ise endpoint add --mac AA:BB:CC:DD:EE:FF --group "Unknown" --dry-run
|
|
42
|
+
|
|
43
|
+
# Output formats
|
|
44
|
+
cisco-ise endpoint list --format json
|
|
45
|
+
cisco-ise endpoint list --format csv
|
|
46
|
+
cisco-ise endpoint list --format toon
|
package/RADIUS.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Get your machine's IP
|
|
2
|
+
ipconfig getifaddr en0
|
|
3
|
+
|
|
4
|
+
# Add it as a NAD on ISE
|
|
5
|
+
cisco-ise network-device add --name "macbook-test" --ip 192.168.40.140 --radius-secret "testing123"
|
|
6
|
+
2. Test RADIUS authentication:
|
|
7
|
+
|
|
8
|
+
You'll need a RADIUS test client. The easiest is radtest from FreeRADIUS:
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
brew install freeradius-server
|
|
12
|
+
|
|
13
|
+
# Test auth against ISE (port 1812)
|
|
14
|
+
radtest admin Cisco123 ise01.automate.builders 0 testing123
|
|
15
|
+
3. Check the logs:
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
cisco-ise radius failures --last 30m
|
|
19
|
+
cisco-ise session list
|
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# cisco-ise
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/cisco-ise)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://buymeacoffee.com/automatebldrs)
|
|
6
|
+
|
|
7
|
+
CLI for Cisco ISE (Identity Services Engine) 3.1+ — day-to-day operations and troubleshooting via ERS, OpenAPI, and MNT APIs.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g cisco-ise
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Add a cluster
|
|
19
|
+
cisco-ise config add lab --host 10.0.0.1 --username admin --password '<ss:ID:password>' --insecure
|
|
20
|
+
|
|
21
|
+
# Test connection
|
|
22
|
+
cisco-ise config test
|
|
23
|
+
|
|
24
|
+
# List endpoints
|
|
25
|
+
cisco-ise endpoint list
|
|
26
|
+
|
|
27
|
+
# Check RADIUS failures
|
|
28
|
+
cisco-ise radius failures --last 1h
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
| Command | Description |
|
|
34
|
+
|---------|-------------|
|
|
35
|
+
| `config` | Manage cluster configurations (add/use/list/show/remove/test/update/clear-cache) |
|
|
36
|
+
| `endpoint` | Manage endpoints (list/search/add/update/delete, CSV bulk import) |
|
|
37
|
+
| `guest` | Manage guest users (list/search/create/extend/suspend/reinstate/delete/portals) |
|
|
38
|
+
| `network-device` | Manage network access devices (list/search/get/add/update/delete) |
|
|
39
|
+
| `session` | Active sessions (list/search/disconnect/reauth via CoA) |
|
|
40
|
+
| `radius` | RADIUS monitoring (failures with human-readable reasons, live polling) |
|
|
41
|
+
| `tacacs` | TACACS+ monitoring (failures/live/command-sets/profiles) |
|
|
42
|
+
| `identity-group` | List identity groups (--type endpoint/user) |
|
|
43
|
+
| `auth-profile` | List/get authorization profiles |
|
|
44
|
+
| `trustsec` | TrustSec SGTs and SGACLs (read-only) |
|
|
45
|
+
| `deployment` | ISE deployment nodes and status (read-only) |
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
### Config file
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cisco-ise config add prod --host ise.example.com --username admin --password '<ss:ID:password>' --insecure --read-only
|
|
53
|
+
cisco-ise config add lab --host 10.0.0.1 --username admin --password '<ss:ID:password>' --insecure
|
|
54
|
+
cisco-ise config use lab
|
|
55
|
+
cisco-ise config list
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Config stored in `~/.cisco-ise/config.json` (mode 0600).
|
|
59
|
+
|
|
60
|
+
### Environment variables
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export CISCO_ISE_HOST=<host>
|
|
64
|
+
export CISCO_ISE_USERNAME=<user>
|
|
65
|
+
export CISCO_ISE_PASSWORD=<password>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Secret Server (ss-cli)
|
|
69
|
+
|
|
70
|
+
Passwords can reference Delinea Secret Server:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
cisco-ise config add prod --host ise.example.com --username admin --password '<ss:1234:password>' --insecure
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Precedence
|
|
77
|
+
|
|
78
|
+
CLI flags > environment variables > config file.
|
|
79
|
+
|
|
80
|
+
## MAC Address Formats
|
|
81
|
+
|
|
82
|
+
Any common format is accepted and automatically normalized to `AA:BB:CC:DD:EE:FF`:
|
|
83
|
+
|
|
84
|
+
- `AA:BB:CC:DD:EE:FF` — colon-separated
|
|
85
|
+
- `AA-BB-CC-DD-EE-FF` — dash-separated
|
|
86
|
+
- `AABB.CCDD.EEFF` — Cisco dot notation
|
|
87
|
+
- `aabbccddeeff` — bare hex
|
|
88
|
+
|
|
89
|
+
## Endpoint Management
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# List all endpoints
|
|
93
|
+
cisco-ise endpoint list
|
|
94
|
+
cisco-ise endpoint list --limit 50 --format json
|
|
95
|
+
|
|
96
|
+
# Search by MAC or group
|
|
97
|
+
cisco-ise endpoint search --mac AA:BB:CC:DD:EE:FF
|
|
98
|
+
cisco-ise endpoint search --group "Profiled"
|
|
99
|
+
|
|
100
|
+
# Add endpoint
|
|
101
|
+
cisco-ise endpoint add --mac AA:BB:CC:DD:EE:FF --group "Unknown" --description "Test device"
|
|
102
|
+
|
|
103
|
+
# Bulk import from CSV
|
|
104
|
+
cisco-ise endpoint add --csv endpoints.csv
|
|
105
|
+
|
|
106
|
+
# Update and delete
|
|
107
|
+
cisco-ise endpoint update AA:BB:CC:DD:EE:FF --group "Profiled"
|
|
108
|
+
cisco-ise endpoint delete AA:BB:CC:DD:EE:FF
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Guest User Management
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# List portals and guests
|
|
115
|
+
cisco-ise guest portals
|
|
116
|
+
cisco-ise guest list
|
|
117
|
+
|
|
118
|
+
# Create a guest
|
|
119
|
+
cisco-ise guest create --first "John" --last "Doe" --email "john@example.com" --portal "Sponsored Guest Portal (default)"
|
|
120
|
+
|
|
121
|
+
# Manage guest lifecycle
|
|
122
|
+
cisco-ise guest extend <id> --duration 1d
|
|
123
|
+
cisco-ise guest suspend <id>
|
|
124
|
+
cisco-ise guest reinstate <id>
|
|
125
|
+
cisco-ise guest delete <id>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Session Management
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# List active sessions
|
|
132
|
+
cisco-ise session list
|
|
133
|
+
cisco-ise session search --mac E2:7C:7E:5B:F0:E0
|
|
134
|
+
cisco-ise session search --user jdoe
|
|
135
|
+
|
|
136
|
+
# CoA operations
|
|
137
|
+
cisco-ise session disconnect E2:7C:7E:5B:F0:E0
|
|
138
|
+
cisco-ise session reauth E2:7C:7E:5B:F0:E0
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## RADIUS & TACACS+ Monitoring
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# RADIUS failures with human-readable reasons
|
|
145
|
+
cisco-ise radius failures --last 1h
|
|
146
|
+
cisco-ise radius failures --last 30m --user jdoe
|
|
147
|
+
|
|
148
|
+
# Live RADIUS monitoring (Ctrl+C to stop)
|
|
149
|
+
cisco-ise radius live
|
|
150
|
+
|
|
151
|
+
# TACACS+
|
|
152
|
+
cisco-ise tacacs failures --last 2h
|
|
153
|
+
cisco-ise tacacs command-sets
|
|
154
|
+
cisco-ise tacacs profiles
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Network Devices
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
cisco-ise network-device list
|
|
161
|
+
cisco-ise network-device search --name "switch"
|
|
162
|
+
cisco-ise network-device add --name "switch01" --ip 10.0.0.1 --radius-secret '<ss:ID:radius-secret>'
|
|
163
|
+
cisco-ise network-device delete "switch01"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Read-Only Protection
|
|
167
|
+
|
|
168
|
+
Clusters marked `--read-only` require typing a random word before any write operation:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
cisco-ise config add prod --host ise.example.com --username admin --password '<ss:ID:password>' --read-only --insecure
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Non-interactive environments are blocked entirely.
|
|
175
|
+
|
|
176
|
+
## Dry Run
|
|
177
|
+
|
|
178
|
+
Preview write operations without executing:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
cisco-ise endpoint add --mac AA:BB:CC:DD:EE:FF --group "Unknown" --dry-run
|
|
182
|
+
# Output: DRY RUN — no changes made
|
|
183
|
+
# POST https://ise.example.com:9060/ers/config/endpoint
|
|
184
|
+
# { ... payload ... }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Output Formats
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
cisco-ise endpoint list --format table # default, human-readable
|
|
191
|
+
cisco-ise endpoint list --format json # for scripting
|
|
192
|
+
cisco-ise endpoint list --format csv # for spreadsheets
|
|
193
|
+
cisco-ise endpoint list --format toon # token-efficient for AI agents
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Global Flags
|
|
197
|
+
|
|
198
|
+
| Flag | Description |
|
|
199
|
+
|------|-------------|
|
|
200
|
+
| `--format <type>` | Output format: table, json, toon, csv |
|
|
201
|
+
| `--host <host>` | ISE hostname or IP |
|
|
202
|
+
| `--username <user>` | ISE username |
|
|
203
|
+
| `--password <pass>` | ISE password |
|
|
204
|
+
| `--cluster <name>` | Use a named cluster |
|
|
205
|
+
| `--insecure` | Skip TLS certificate verification |
|
|
206
|
+
| `--read-only` | Block write operations |
|
|
207
|
+
| `--dry-run` | Show what would happen without executing |
|
|
208
|
+
| `--no-audit` | Disable audit logging |
|
|
209
|
+
| `--no-cache` | Bypass response cache |
|
|
210
|
+
| `--debug` | Enable debug logging |
|
|
211
|
+
|
|
212
|
+
## ISE API Details
|
|
213
|
+
|
|
214
|
+
- **ERS API** (port 9060) — endpoints, groups, network devices, guests, auth profiles, TrustSec
|
|
215
|
+
- **OpenAPI** (port 443) — deployment, modern endpoints
|
|
216
|
+
- **MNT API** (port 443) — sessions, CoA, RADIUS/TACACS monitoring
|
|
217
|
+
|
|
218
|
+
ERS must be enabled in ISE Admin > Settings > ERS Settings.
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
MIT
|
package/bin/cisco-ise.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Suppress Node.js TLS warning — users opt into insecure mode explicitly
|
|
4
|
+
// via --insecure flag or cluster config, so the warning is redundant noise
|
|
5
|
+
const originalEmit = process.emit;
|
|
6
|
+
process.emit = function (event, warning) {
|
|
7
|
+
if (event === "warning" && warning?.name === "Warning" &&
|
|
8
|
+
warning?.message?.includes("NODE_TLS_REJECT_UNAUTHORIZED")) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return originalEmit.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
require("../cli/index.js");
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { resolveConnection } = require("../utils/connection.js");
|
|
2
|
+
const { printResult, printError } = require("../utils/output.js");
|
|
3
|
+
const IseClient = require("../utils/api.js");
|
|
4
|
+
|
|
5
|
+
module.exports = function (program) {
|
|
6
|
+
const cmd = program.command("auth-profile").description("List ISE authorization profiles");
|
|
7
|
+
|
|
8
|
+
cmd.command("list")
|
|
9
|
+
.description("List all authorization profiles")
|
|
10
|
+
.action(async (opts, command) => {
|
|
11
|
+
try {
|
|
12
|
+
const globalOpts = command.optsWithGlobals();
|
|
13
|
+
const conn = resolveConnection(globalOpts);
|
|
14
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
15
|
+
|
|
16
|
+
const resources = await client.ersPaginateAll("/authorizationprofile");
|
|
17
|
+
await printResult(resources, globalOpts.format);
|
|
18
|
+
} catch (err) { printError(err); }
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
cmd.command("get <name>")
|
|
22
|
+
.description("Get authorization profile details")
|
|
23
|
+
.action(async (name, opts, command) => {
|
|
24
|
+
try {
|
|
25
|
+
const globalOpts = command.optsWithGlobals();
|
|
26
|
+
const conn = resolveConnection(globalOpts);
|
|
27
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
28
|
+
|
|
29
|
+
const data = await client.ersGet("/authorizationprofile", { filter: `name.EQ.${name}`, size: 1 });
|
|
30
|
+
const resources = data?.SearchResult?.resources;
|
|
31
|
+
if (!resources?.length) throw new Error(`Authorization profile "${name}" not found.`);
|
|
32
|
+
const detail = await client.ersGet(`/authorizationprofile/${resources[0].id}`);
|
|
33
|
+
await printResult(detail?.AuthorizationProfile || detail, globalOpts.format);
|
|
34
|
+
} catch (err) { printError(err); }
|
|
35
|
+
});
|
|
36
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const { resolveConnection } = require("../utils/connection.js");
|
|
2
|
+
const config = require("../utils/config.js");
|
|
3
|
+
const { printResult, printError } = require("../utils/output.js");
|
|
4
|
+
const audit = require("../utils/audit.js");
|
|
5
|
+
const IseClient = require("../utils/api.js");
|
|
6
|
+
|
|
7
|
+
module.exports = function (program) {
|
|
8
|
+
const cmd = program.command("config").description("Manage ISE cluster configurations");
|
|
9
|
+
|
|
10
|
+
cmd.command("add <name>")
|
|
11
|
+
.description("Add a named ISE cluster")
|
|
12
|
+
.action((name, opts, command) => {
|
|
13
|
+
try {
|
|
14
|
+
const globalOpts = command.optsWithGlobals();
|
|
15
|
+
const { host, username, password, ppan, pmnt, insecure, readOnly } = globalOpts;
|
|
16
|
+
if (!host) throw new Error("--host is required");
|
|
17
|
+
if (!username) throw new Error("--username is required");
|
|
18
|
+
if (!password) throw new Error("--password is required");
|
|
19
|
+
config.addCluster(name, { host, username, password, ppan, pmnt, insecure, readOnly });
|
|
20
|
+
console.log(`Cluster "${name}" added successfully.`);
|
|
21
|
+
} catch (err) { printError(err); }
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
cmd.command("use <name>")
|
|
25
|
+
.description("Set the active cluster")
|
|
26
|
+
.action((name) => {
|
|
27
|
+
try {
|
|
28
|
+
config.useCluster(name);
|
|
29
|
+
console.log(`Active cluster set to "${name}".`);
|
|
30
|
+
} catch (err) { printError(err); }
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
cmd.command("list")
|
|
34
|
+
.description("List all configured clusters")
|
|
35
|
+
.action(async (opts, command) => {
|
|
36
|
+
try {
|
|
37
|
+
const clusters = config.listClusters();
|
|
38
|
+
const active = config.loadConfig().activeCluster;
|
|
39
|
+
const rows = Object.entries(clusters).map(([name, c]) => ({
|
|
40
|
+
name: name === active ? `${name} *` : name,
|
|
41
|
+
host: c.host,
|
|
42
|
+
username: c.username,
|
|
43
|
+
readOnly: c.readOnly ? "yes" : "no",
|
|
44
|
+
}));
|
|
45
|
+
await printResult(rows, command.optsWithGlobals().format);
|
|
46
|
+
} catch (err) { printError(err); }
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
cmd.command("show")
|
|
50
|
+
.description("Show active cluster details")
|
|
51
|
+
.action(async (opts, command) => {
|
|
52
|
+
try {
|
|
53
|
+
const data = config.loadConfig();
|
|
54
|
+
const cluster = data.clusters[data.activeCluster];
|
|
55
|
+
if (!cluster) throw new Error("No active cluster. Run: cisco-ise config add <name> ...");
|
|
56
|
+
const details = {
|
|
57
|
+
name: data.activeCluster,
|
|
58
|
+
host: cluster.host,
|
|
59
|
+
username: cluster.username,
|
|
60
|
+
password: config.maskPassword(cluster.password),
|
|
61
|
+
insecure: cluster.insecure || false,
|
|
62
|
+
readOnly: cluster.readOnly || false,
|
|
63
|
+
};
|
|
64
|
+
if (cluster.ppan) details.ppan = cluster.ppan;
|
|
65
|
+
if (cluster.pmnt) details.pmnt = cluster.pmnt;
|
|
66
|
+
if (cluster.sponsorUser) details.sponsorUser = cluster.sponsorUser;
|
|
67
|
+
if (cluster.sponsorPassword) details.sponsorPassword = config.maskPassword(cluster.sponsorPassword);
|
|
68
|
+
await printResult(details, command.optsWithGlobals().format);
|
|
69
|
+
} catch (err) { printError(err); }
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
cmd.command("remove <name>")
|
|
73
|
+
.description("Remove a cluster from config")
|
|
74
|
+
.action((name) => {
|
|
75
|
+
try {
|
|
76
|
+
config.removeCluster(name);
|
|
77
|
+
console.log(`Cluster "${name}" removed.`);
|
|
78
|
+
} catch (err) { printError(err); }
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
cmd.command("test")
|
|
82
|
+
.description("Test connection to active cluster")
|
|
83
|
+
.action(async (opts, command) => {
|
|
84
|
+
try {
|
|
85
|
+
const globalOpts = command.optsWithGlobals();
|
|
86
|
+
const conn = resolveConnection(globalOpts);
|
|
87
|
+
const client = new IseClient(conn, { debug: globalOpts.debug });
|
|
88
|
+
|
|
89
|
+
let ersOk = false, openApiOk = false;
|
|
90
|
+
try {
|
|
91
|
+
await client.ersGet("/endpoint", { size: 1 });
|
|
92
|
+
ersOk = true;
|
|
93
|
+
} catch { /* ERS not available */ }
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
await client.openApiGet("/deployment/node");
|
|
97
|
+
openApiOk = true;
|
|
98
|
+
} catch { /* OpenAPI not available */ }
|
|
99
|
+
|
|
100
|
+
if (!ersOk && !openApiOk) {
|
|
101
|
+
throw new Error("Connection failed. Check host, credentials, and that ERS is enabled.");
|
|
102
|
+
}
|
|
103
|
+
console.log("Connection successful.");
|
|
104
|
+
console.log(` ERS API (port 9060): ${ersOk ? "\u2713" : "\u2717"}`);
|
|
105
|
+
console.log(` OpenAPI (port 443): ${openApiOk ? "\u2713" : "\u2717"}`);
|
|
106
|
+
} catch (err) { printError(err); }
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
cmd.command("update <name>")
|
|
110
|
+
.description("Update cluster settings")
|
|
111
|
+
.action((name, opts, command) => {
|
|
112
|
+
try {
|
|
113
|
+
const globalOpts = command.optsWithGlobals();
|
|
114
|
+
const updates = {};
|
|
115
|
+
if (globalOpts.host) updates.host = globalOpts.host;
|
|
116
|
+
if (globalOpts.username) updates.username = globalOpts.username;
|
|
117
|
+
if (globalOpts.password) updates.password = globalOpts.password;
|
|
118
|
+
if (globalOpts.ppan) updates.ppan = globalOpts.ppan;
|
|
119
|
+
if (globalOpts.pmnt) updates.pmnt = globalOpts.pmnt;
|
|
120
|
+
if (globalOpts.sponsorUser) updates.sponsorUser = globalOpts.sponsorUser;
|
|
121
|
+
if (globalOpts.sponsorPassword) updates.sponsorPassword = globalOpts.sponsorPassword;
|
|
122
|
+
if (globalOpts.insecure !== undefined) updates.insecure = globalOpts.insecure;
|
|
123
|
+
if (globalOpts.readOnly !== undefined) updates.readOnly = globalOpts.readOnly;
|
|
124
|
+
config.updateCluster(name, updates);
|
|
125
|
+
console.log(`Cluster "${name}" updated.`);
|
|
126
|
+
} catch (err) { printError(err); }
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
cmd.command("clear-cache")
|
|
130
|
+
.description("Clear response cache")
|
|
131
|
+
.action((opts, command) => {
|
|
132
|
+
try {
|
|
133
|
+
const globalOpts = command.optsWithGlobals();
|
|
134
|
+
const conn = resolveConnection(globalOpts);
|
|
135
|
+
const client = new IseClient(conn);
|
|
136
|
+
client.invalidateCache();
|
|
137
|
+
console.log("Cache cleared.");
|
|
138
|
+
} catch (err) { printError(err); }
|
|
139
|
+
});
|
|
140
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { resolveConnection } = require("../utils/connection.js");
|
|
2
|
+
const { printResult, printError } = require("../utils/output.js");
|
|
3
|
+
const IseClient = require("../utils/api.js");
|
|
4
|
+
|
|
5
|
+
module.exports = function (program) {
|
|
6
|
+
const cmd = program.command("deployment").description("ISE deployment information (read-only)");
|
|
7
|
+
|
|
8
|
+
cmd.command("nodes")
|
|
9
|
+
.description("List ISE deployment nodes")
|
|
10
|
+
.action(async (opts, command) => {
|
|
11
|
+
try {
|
|
12
|
+
const globalOpts = command.optsWithGlobals();
|
|
13
|
+
const conn = resolveConnection(globalOpts);
|
|
14
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
15
|
+
|
|
16
|
+
const data = await client.openApiGet("/deployment/node");
|
|
17
|
+
const nodes = data?.response || data || [];
|
|
18
|
+
const result = Array.isArray(nodes) ? nodes : [nodes];
|
|
19
|
+
await printResult(result.map((n) => ({
|
|
20
|
+
hostname: n.hostname || n.fqdn || "",
|
|
21
|
+
fqdn: n.fqdn || "",
|
|
22
|
+
ip: n.ipAddress || n.ipAddresses?.[0] || "",
|
|
23
|
+
roles: Array.isArray(n.roles) ? n.roles.join(", ") : (n.roles || ""),
|
|
24
|
+
services: Array.isArray(n.services) ? n.services.join(", ") : (n.services || ""),
|
|
25
|
+
status: n.nodeStatus || "",
|
|
26
|
+
})), globalOpts.format);
|
|
27
|
+
} catch (err) { printError(err); }
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
cmd.command("status")
|
|
31
|
+
.description("Show ISE deployment status")
|
|
32
|
+
.action(async (opts, command) => {
|
|
33
|
+
try {
|
|
34
|
+
const globalOpts = command.optsWithGlobals();
|
|
35
|
+
const conn = resolveConnection(globalOpts);
|
|
36
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
37
|
+
|
|
38
|
+
const data = await client.openApiGet("/deployment/node");
|
|
39
|
+
const nodes = data?.response || data || [];
|
|
40
|
+
const result = Array.isArray(nodes) ? nodes : [nodes];
|
|
41
|
+
await printResult(result.map((n) => ({
|
|
42
|
+
hostname: n.hostname || n.fqdn || "",
|
|
43
|
+
status: n.nodeStatus || "unknown",
|
|
44
|
+
roles: Array.isArray(n.roles) ? n.roles.join(", ") : (n.roles || ""),
|
|
45
|
+
services: Array.isArray(n.services) ? n.services.join(", ") : (n.services || ""),
|
|
46
|
+
})), globalOpts.format);
|
|
47
|
+
} catch (err) { printError(err); }
|
|
48
|
+
});
|
|
49
|
+
};
|