Adds `debug snapshot --id <int>` to query a running instance's current attribute values and alarm states without the subscribe/stream overhead of the debug view. Routes through ManagementActor → CommunicationService → site DeploymentManager → InstanceActor using the existing remote query pattern.
268 lines
12 KiB
Markdown
268 lines
12 KiB
Markdown
# Component: CLI
|
|
|
|
## Purpose
|
|
|
|
The CLI is a standalone command-line tool for scripting and automating administrative operations against the ScadaLink central cluster. It connects to the ManagementActor via Akka.NET ClusterClient — it does not join the cluster as a full member and does not use HTTP/REST. The CLI provides the same administrative capabilities as the Central UI, enabling automation, batch operations, and integration with CI/CD pipelines.
|
|
|
|
## Location
|
|
|
|
Standalone executable, not part of the Host binary. Deployed on any Windows machine with network access to the central cluster.
|
|
|
|
`src/ScadaLink.CLI/`
|
|
|
|
## Responsibilities
|
|
|
|
- Parse command-line arguments and dispatch to the appropriate management operation.
|
|
- Authenticate the user via LDAP credentials and include identity in every message sent to the ManagementActor.
|
|
- Connect to the central cluster via Akka.NET ClusterClient using configured contact points.
|
|
- Send management messages to the ManagementActor and display structured responses.
|
|
- Support both JSON and human-readable table output formats.
|
|
|
|
## Technology
|
|
|
|
- **Argument parsing**: `System.CommandLine` library for command/subcommand/option parsing with built-in help generation.
|
|
- **Transport**: Akka.NET `ClusterClient` connecting to `ClusterClientReceptionist` on the central cluster. The CLI does not join the cluster — it is a lightweight external client.
|
|
- **Serialization**: Message contracts from Commons (`Messages/Management/`), same as ManagementActor expects.
|
|
|
|
## Authentication
|
|
|
|
The CLI authenticates the user against LDAP/AD before any operation:
|
|
|
|
1. The user provides credentials via `--username` / `--password` options, or is prompted interactively if omitted.
|
|
2. The CLI performs a direct LDAP bind against the configured LDAP server (same mechanism as the Central UI login).
|
|
3. On successful bind, the CLI queries group memberships to determine roles and permitted sites.
|
|
4. Every message sent to the ManagementActor includes the `AuthenticatedUser` envelope with the user's identity, roles, and site permissions.
|
|
5. Credentials are not stored or cached between invocations. Each CLI invocation requires fresh authentication.
|
|
|
|
LDAP connection settings are read from the CLI configuration (see Configuration section).
|
|
|
|
## Connection
|
|
|
|
The CLI uses Akka.NET ClusterClient to connect to the central cluster:
|
|
|
|
- **Contact points**: One or more seed node addresses for the ClusterClientReceptionist. The CLI sends an initial contact to these addresses; the receptionist responds with the current set of cluster nodes hosting the ManagementActor.
|
|
- **No cluster membership**: The CLI does not join the Akka.NET cluster. It is an external process that communicates via the ClusterClient protocol.
|
|
- **Failover**: If the active central node fails over, ClusterClient transparently reconnects to the new active node via the receptionist. In-flight commands may time out and need to be retried.
|
|
|
|
## Command Structure
|
|
|
|
The CLI uses a hierarchical subcommand structure mirroring the Management Service message groups:
|
|
|
|
```
|
|
scadalink <group> <action> [options]
|
|
```
|
|
|
|
### Template Commands
|
|
```
|
|
scadalink template list [--format json|table]
|
|
scadalink template get <name> [--format json|table]
|
|
scadalink template create --name <name> [--parent <parent>] --file <path>
|
|
scadalink template update <name> --file <path>
|
|
scadalink template delete <name>
|
|
scadalink template validate <name>
|
|
scadalink template diff <instance-code>
|
|
scadalink template attribute add --template-id <id> --name <name> --data-type <type> [--default-value <value>] [--tag-path <path>]
|
|
scadalink template attribute update --template-id <id> --name <name> [--data-type <type>] [--default-value <value>] [--tag-path <path>]
|
|
scadalink template attribute delete --template-id <id> --name <name>
|
|
scadalink template alarm add --template-id <id> --name <name> --trigger-attribute <attr> --condition <cond> --setpoint <value> [--severity <level>] [--notification-list <name>]
|
|
scadalink template alarm update --template-id <id> --name <name> [--condition <cond>] [--setpoint <value>] [--severity <level>] [--notification-list <name>]
|
|
scadalink template alarm delete --template-id <id> --name <name>
|
|
scadalink template script add --template-id <id> --name <name> --trigger-type <type> [--trigger-attribute <attr>] [--interval <ms>] --code <code>
|
|
scadalink template script update --template-id <id> --name <name> [--trigger-type <type>] [--trigger-attribute <attr>] [--interval <ms>] [--code <code>]
|
|
scadalink template script delete --template-id <id> --name <name>
|
|
scadalink template composition add --template-id <id> --module-template-id <id> --instance-name <name>
|
|
scadalink template composition delete --template-id <id> --instance-name <name>
|
|
```
|
|
|
|
### Instance Commands
|
|
```
|
|
scadalink instance list [--site <site>] [--area <area>] [--format json|table]
|
|
scadalink instance get <code> [--format json|table]
|
|
scadalink instance create --template <name> --site <site> --code <code> [--area <area>]
|
|
scadalink instance set-overrides <code> --file <path>
|
|
scadalink instance set-bindings <code> --bindings <json>
|
|
scadalink instance bind-connections <code> --file <path>
|
|
scadalink instance assign-area <code> --area <area>
|
|
scadalink instance enable <code>
|
|
scadalink instance disable <code>
|
|
scadalink instance delete <code>
|
|
```
|
|
|
|
### Site Commands
|
|
```
|
|
scadalink site list [--format json|table]
|
|
scadalink site get <site-id> [--format json|table]
|
|
scadalink site create --name <name> --id <site-id>
|
|
scadalink site update <site-id> --file <path>
|
|
scadalink site delete <site-id>
|
|
scadalink site area list <site-id>
|
|
scadalink site area create <site-id> --name <name> [--parent <parent-area>]
|
|
scadalink site area update <site-id> --name <name> [--new-name <name>] [--parent <parent-area>]
|
|
scadalink site area delete <site-id> --name <name>
|
|
```
|
|
|
|
### Deployment Commands
|
|
```
|
|
scadalink deploy instance <code>
|
|
scadalink deploy artifacts [--site <site>] [--type <artifact-type>]
|
|
scadalink deploy status [--format json|table]
|
|
```
|
|
|
|
### Data Connection Commands
|
|
```
|
|
scadalink data-connection list [--format json|table]
|
|
scadalink data-connection get <name> [--format json|table]
|
|
scadalink data-connection create --file <path>
|
|
scadalink data-connection update <name> --file <path>
|
|
scadalink data-connection delete <name>
|
|
scadalink data-connection assign <name> --site <site-id>
|
|
scadalink data-connection unassign <name> --site <site-id>
|
|
```
|
|
|
|
### External System Commands
|
|
```
|
|
scadalink external-system list [--format json|table]
|
|
scadalink external-system get <name> [--format json|table]
|
|
scadalink external-system create --file <path>
|
|
scadalink external-system update <name> --file <path>
|
|
scadalink external-system delete <name>
|
|
```
|
|
|
|
### Notification Commands
|
|
```
|
|
scadalink notification list [--format json|table]
|
|
scadalink notification get <name> [--format json|table]
|
|
scadalink notification create --file <path>
|
|
scadalink notification update <name> --file <path>
|
|
scadalink notification delete <name>
|
|
scadalink notification smtp list [--format json|table]
|
|
scadalink notification smtp update --file <path>
|
|
```
|
|
|
|
### Security Commands
|
|
```
|
|
scadalink security api-key list [--format json|table]
|
|
scadalink security api-key create --name <name>
|
|
scadalink security api-key update <name> [--name <new-name>] [--enabled <bool>]
|
|
scadalink security api-key enable <name>
|
|
scadalink security api-key disable <name>
|
|
scadalink security api-key delete <name>
|
|
scadalink security role-mapping list [--format json|table]
|
|
scadalink security role-mapping create --group <ldap-group> --role <role> [--site <site>]
|
|
scadalink security role-mapping update --id <id> [--group <ldap-group>] [--role <role>]
|
|
scadalink security role-mapping delete --group <ldap-group> --role <role>
|
|
scadalink security scope-rule list [--role-mapping-id <id>] [--format json|table]
|
|
scadalink security scope-rule add --role-mapping-id <id> --site-id <site-id>
|
|
scadalink security scope-rule delete --id <id>
|
|
```
|
|
|
|
### Audit Log Commands
|
|
```
|
|
scadalink audit-log query [--user <username>] [--entity-type <type>] [--from <date>] [--to <date>] [--format json|table]
|
|
```
|
|
|
|
### Health Commands
|
|
```
|
|
scadalink health summary [--format json|table]
|
|
scadalink health site <site-id> [--format json|table]
|
|
scadalink health event-log --site-identifier <site-id> [--from <date>] [--to <date>] [--search <term>] [--page <n>] [--page-size <n>] [--format json|table]
|
|
scadalink health parked-messages --site-identifier <site-id> [--page <n>] [--page-size <n>] [--format json|table]
|
|
```
|
|
|
|
### Debug Commands
|
|
```
|
|
scadalink debug snapshot --id <id> [--format json|table]
|
|
```
|
|
|
|
### Shared Script Commands
|
|
```
|
|
scadalink shared-script list [--format json|table]
|
|
scadalink shared-script get --id <id> [--format json|table]
|
|
scadalink shared-script create --name <name> --code <code>
|
|
scadalink shared-script update --id <id> [--name <name>] [--code <code>]
|
|
scadalink shared-script delete --id <id>
|
|
```
|
|
|
|
### Database Connection Commands
|
|
```
|
|
scadalink db-connection list [--format json|table]
|
|
scadalink db-connection get --id <id> [--format json|table]
|
|
scadalink db-connection create --name <name> --connection-string <string> [--provider <provider>]
|
|
scadalink db-connection update --id <id> [--name <name>] [--connection-string <string>] [--provider <provider>]
|
|
scadalink db-connection delete --id <id>
|
|
```
|
|
|
|
### Inbound API Method Commands
|
|
```
|
|
scadalink api-method list [--format json|table]
|
|
scadalink api-method get --id <id> [--format json|table]
|
|
scadalink api-method create --name <name> --code <code> [--description <desc>]
|
|
scadalink api-method update --id <id> [--name <name>] [--code <code>] [--description <desc>]
|
|
scadalink api-method delete --id <id>
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Configuration is resolved in the following priority order (highest wins):
|
|
|
|
1. **Command-line options**: `--contact-points`, `--username`, `--password`, `--format`.
|
|
2. **Environment variables**:
|
|
- `SCADALINK_CONTACT_POINTS` — Comma-separated list of central cluster contact point addresses (e.g., `akka.tcp://ScadaLink@central1:8081,akka.tcp://ScadaLink@central2:8081`).
|
|
- `SCADALINK_LDAP_SERVER` — LDAP server address.
|
|
- `SCADALINK_LDAP_PORT` — LDAP port (default: 636 for LDAPS).
|
|
- `SCADALINK_FORMAT` — Default output format (`json` or `table`).
|
|
3. **Configuration file**: `~/.scadalink/config.json` — Persistent defaults for contact points, LDAP settings, and output format.
|
|
|
|
### Configuration File Format
|
|
|
|
```json
|
|
{
|
|
"contactPoints": [
|
|
"akka.tcp://ScadaLink@central1:8081",
|
|
"akka.tcp://ScadaLink@central2:8081"
|
|
],
|
|
"ldap": {
|
|
"server": "ad.example.com",
|
|
"port": 636,
|
|
"useTls": true
|
|
},
|
|
"defaultFormat": "json"
|
|
}
|
|
```
|
|
|
|
## Output Formats
|
|
|
|
- **JSON** (default): Machine-readable JSON output to stdout. Suitable for piping to `jq` or processing in scripts. Errors are written to stderr as JSON objects with `error` and `code` fields.
|
|
- **Table** (`--format table` or `--table`): Human-readable tabular output with aligned columns. Suitable for interactive use.
|
|
|
|
## Exit Codes
|
|
|
|
| Code | Meaning |
|
|
|------|---------|
|
|
| 0 | Success |
|
|
| 1 | General error (command failed) |
|
|
| 2 | Authentication failure (LDAP bind failed) |
|
|
| 3 | Authorization failure (insufficient role) |
|
|
| 4 | Connection failure (cannot reach central cluster) |
|
|
| 5 | Validation failure (e.g., template validation errors) |
|
|
|
|
## Error Handling
|
|
|
|
- **Connection failure**: If the CLI cannot establish a ClusterClient connection within a timeout (default 10 seconds), it exits with code 4 and a descriptive error message.
|
|
- **Command timeout**: If the ManagementActor does not respond within 30 seconds (configurable), the command fails with a timeout error.
|
|
- **Authentication failure**: If the LDAP bind fails, the CLI exits with code 2 before sending any commands.
|
|
- **Authorization failure**: If the ManagementActor returns an Unauthorized response, the CLI exits with code 3.
|
|
|
|
## Dependencies
|
|
|
|
- **Commons**: Message contracts (`Messages/Management/`), shared types.
|
|
- **System.CommandLine**: Command-line argument parsing.
|
|
- **Akka.NET (Akka.Cluster.Tools)**: ClusterClient for communication with the central cluster.
|
|
- **LDAP client library**: For direct LDAP bind authentication (same approach as Security & Auth component).
|
|
|
|
## Interactions
|
|
|
|
- **Management Service (ManagementActor)**: The CLI's sole runtime dependency. All operations are sent as messages to the ManagementActor via ClusterClient.
|
|
- **Security & Auth**: The CLI performs LDAP authentication independently (same LDAP server, same bind mechanism) and passes the authenticated identity to the ManagementActor. The ManagementActor enforces authorization.
|
|
- **LDAP/Active Directory**: Direct bind for user authentication before any operation.
|