How to change config at runtime - IBM/ibm-spectrum-scale-bridge-for-grafana GitHub Wiki
The IBM Storage Scale Bridge for Grafana exposes a /config REST API that lets
you read and update selected configuration parameters while the bridge is
running — without editing files. Some parameters take effect immediately;
others are written to the config file and require a bridge restart to apply.
The API is mounted on the same port as the OpenTSDB or Prometheus endpoint and is protected by the same HTTP Basic Authentication that guards all other bridge endpoints. No extra setup is required.
Configuration parameters are grouped the same way they appear in config.ini:
each parameter belongs to a section (e.g. prometheues_exporter_plugin,
logging). The full config view always returns this sectioned structure so
the output maps directly back to the file you would otherwise edit by hand.
Parameters that are commented-out in config.ini (i.e. not yet active) appear
in the API response with the value "disabled" so you can see the complete
picture at a glance.
Secret values (password, apiKeyValue) are never returned or accepted by the
API.
| Method | URL | Description |
|---|---|---|
GET |
/config |
All non-secret settings, grouped by INI section |
GET |
/config?key=<name> |
A single setting (flat {key: value}) |
PATCH |
/config |
Batch-update one or more writable keys |
PUT |
/config?key=<name> |
Update a single writable key |
GET |
/config/sections |
List available INI section names |
GET |
/config/section/<section> |
All non-secret keys in one INI section |
PATCH |
/config/section/<section> |
Update writable keys scoped to one INI section |
POST |
/config/init |
Create the custom config file so writes are persisted |
GET |
/config/validate |
Validate the current live configuration |
POST |
/config/validate |
Dry-run: validate a proposed change without applying it |
Each configuration key falls into exactly one of the following categories, which determines what the API will do with it.
These keys are safe to change while the bridge is running. The new value takes effect in the current process immediately and is also written back to the custom config file so it survives a restart.
| Key | Section | Description |
|---|---|---|
rawCounters |
prometheues_exporter_plugin |
Export raw sensor counter values (true / false) |
logLevel |
logging |
Active log level (see table below) |
includeDiskData |
query |
Use historical disk data (true / false) |
retryDelay |
server |
Seconds before retrying a failed metadata fetch |
Log levels
| Value | Name |
|---|---|
5 |
TRACE |
10 |
DEBUG |
15 |
MOREINFO |
20 |
INFO |
30 |
WARN |
40 |
ERROR |
These keys are tied to bound server sockets, open file handles, or TLS
connections that cannot be changed while the bridge is running. The API
always writes the new value to the config file and returns a
"requires restart" message in the rejected field. Restart the bridge to
apply the change.
Sending null as the value removes the key from the config file entirely,
which disables the corresponding feature on the next restart.
This category includes: prometheus, port, protocol, server,
serverPort, opentsdbBindIp, promBindIp, tlsKeyPath, tlsKeyFile,
tlsCertFile, caCertPath, logPath, logFile, cpAccessLog,
cpAccessLogBackups.
Hidden — never readable or writable
password and apiKeyValue are never returned in any response and are
silently rejected in write requests.
GET /config returns a JSON object keyed by INI section name. Each section
contains its keys with their current values. A value of "disabled" means
the key is known to the configuration schema but was not set (still
commented-out in the INI file).
{
"basic_auth": {
"enabled": true,
"username": "scale_admin"
},
"connection": {
"protocol": "http"
},
"logging": {
"cpAccessLog": "on",
"cpAccessLogBackups": 10,
"logFile": "zserver.log",
"logLevel": 15,
"logPath": "/var/log/ibm_bridge_for_grafana"
},
"opentsdb_plugin": {
"opentsdbBindIp": "0.0.0.0",
"port": 4242
},
"prometheues_exporter_plugin": {
"promBindIp": "0.0.0.0",
"prometheus": "disabled",
"rawCounters": true
},
"query": {
"includeDiskData": false
},
"server": {
"apiKeyName": "scale_grafana",
"caCertPath": false,
"retryDelay": 60,
"server": "localhost",
"serverPort": 9980
},
"tls": {
"tlsCertFile": "disabled",
"tlsKeyFile": "disabled",
"tlsKeyPath": "disabled"
}
}In this example the bridge was started without a Prometheus port (prometheus
is "disabled") and without TLS certificates (all three tls keys are
"disabled").
All write endpoints return the same JSON structure:
{
"updated": { "rawCounters": false },
"restart_required": { "port": "read-only: restart the bridge to apply this change" },
"rejected": {},
"persisted": true
}| Field | Type | Description |
|---|---|---|
updated |
object | Keys applied to the running process immediately |
restart_required |
object | Keys written to the config file but requiring a bridge restart to take effect |
rejected |
object | Keys refused entirely (secret / unknown / read-only), with a reason string each |
persisted |
true/false/str |
true = written to file; false = I/O error; string = why writing was skipped |
persisted is a descriptive string (not false) when writing is skipped for a
known reason:
- No custom config file exists → call
POST /config/initto create one - The template
config.iniwas passed as-F→ create a separate custom file
All examples assume the bridge is listening on port 4242 and Basic Auth is
enabled with the default username scale_admin.
curl -s -u scale_admin:MyPassword \
http://scale-bridge.example.com:4242/config \
| python3 -m json.toolcurl -s -u scale_admin:MyPassword \
"http://scale-bridge.example.com:4242/config?key=logLevel"{ "logLevel": 15 }curl -s -u scale_admin:MyPassword \
http://scale-bridge.example.com:4242/config/sections{
"sections": [
"basic_auth", "connection", "logging",
"opentsdb_plugin", "prometheues_exporter_plugin",
"query", "server", "tls"
]
}curl -s -u scale_admin:MyPassword \
http://scale-bridge.example.com:4242/config/section/prometheues_exporter_plugin{
"promBindIp": "0.0.0.0",
"prometheus": "disabled",
"rawCounters": true
}curl -s -u scale_admin:MyPassword \
-X PUT \
-H "Content-Type: application/json" \
-d '{"value": 5}' \
"http://scale-bridge.example.com:4242/config?key=logLevel"{
"updated": { "logLevel": 5 },
"restart_required": {},
"rejected": {},
"persisted": true
}The bridge logs at TRACE level immediately — no restart required.
To revert to the default MOREINFO level:
curl -s -u scale_admin:MyPassword \
-X PUT \
-H "Content-Type: application/json" \
-d '{"value": 15}' \
"http://scale-bridge.example.com:4242/config?key=logLevel"curl -s -u scale_admin:MyPassword \
-X PUT \
-H "Content-Type: application/json" \
-d '{"value": false}' \
"http://scale-bridge.example.com:4242/config?key=rawCounters"{
"updated": { "rawCounters": false },
"restart_required": {},
"rejected": {},
"persisted": true
}curl -s -u scale_admin:MyPassword \
-X PATCH \
-H "Content-Type: application/json" \
-d '{"rawCounters": false, "logLevel": 20, "retryDelay": 30}' \
http://scale-bridge.example.com:4242/config{
"updated": { "rawCounters": false, "logLevel": 20, "retryDelay": 30 },
"restart_required": {},
"rejected": {},
"persisted": true
}curl -s -u scale_admin:MyPassword \
-X PATCH \
-H "Content-Type: application/json" \
-d '{"rawCounters": false}' \
http://scale-bridge.example.com:4242/config/section/prometheues_exporter_plugin{
"section": "prometheues_exporter_plugin",
"updated": { "rawCounters": false },
"restart_required": {},
"rejected": {},
"persisted": true
}Keys sent to the wrong section endpoint are rejected immediately with a clear reason before any update is applied:
curl -s -u scale_admin:MyPassword \
-X PATCH \
-H "Content-Type: application/json" \
-d '{"rawCounters": false, "logLevel": 10}' \
http://scale-bridge.example.com:4242/config/section/prometheues_exporter_plugin{
"section": "prometheues_exporter_plugin",
"updated": { "rawCounters": false },
"restart_required": {},
"rejected": { "logLevel": "key does not belong to section 'prometheues_exporter_plugin'" },
"persisted": true
}The Prometheus port is bound at startup and cannot be changed in a live
process. The API writes the value to the config file and returns it in
restart_required:
curl -s -u scale_admin:MyPassword \
-X PUT \
-H "Content-Type: application/json" \
-d '{"value": 9250}' \
"http://scale-bridge.example.com:4242/config?key=prometheus"{
"updated": {},
"restart_required": {
"prometheus": "read-only: restart the bridge to apply this change"
},
"rejected": {},
"persisted": true
}After restarting the bridge the Prometheus exporter starts listening on
port 9250.
Pass null to remove the key from the config file. The feature will be
inactive after the next restart.
curl -s -u scale_admin:MyPassword \
-X PATCH \
-H "Content-Type: application/json" \
-d '{"port": null}' \
http://scale-bridge.example.com:4242/config/section/opentsdb_plugin{
"section": "opentsdb_plugin",
"updated": {},
"restart_required": {
"port": "read-only: restart the bridge to apply this change"
},
"rejected": {},
"persisted": true
}The port line is removed from custom.ini. After restarting, the bridge
starts without an OpenTSDB listener. To re-enable it later:
curl -s -u scale_admin:MyPassword \
-X PATCH \
-H "Content-Type: application/json" \
-d '{"port": 4242}' \
http://scale-bridge.example.com:4242/config/section/opentsdb_pluginIf the bridge was started without a custom config file, write calls succeed
in-memory but return "persisted": false and log a warning. Call this
endpoint once to create the file — no restart required.
Without a body the file is created at the default RPM location
(/etc/grafanabridge/config.ini). Pass a "path" key to choose a different
location:
# Default location:
curl -s -u scale_admin:MyPassword \
-X POST \
http://scale-bridge.example.com:4242/config/init
# Custom location:
curl -s -u scale_admin:MyPassword \
-X POST \
-H "Content-Type: application/json" \
-d '{"path": "/opt/bridge/myconfig.ini"}' \
http://scale-bridge.example.com:4242/config/initFile created at the default location (201 Created):
{ "created": true, "path": "/etc/grafanabridge/config.ini" }All subsequent PATCH / PUT writes are persisted immediately — no restart
required.
File created at a custom path (201 Created):
{
"created": true,
"path": "/opt/bridge/myconfig.ini",
"note": "Restart required: update your service to add '-F /opt/bridge/myconfig.ini' and restart the bridge before writes are persisted to this file."
}A custom path is not automatically picked up by the running bridge. Update the systemd unit and restart before subsequent writes will be persisted:
# Add -F to the service ExecStart line:
systemctl edit grafana-bridge
# → Add: ExecStart=... -F /opt/bridge/myconfig.ini
systemctl daemon-reload
systemctl restart grafana-bridgeFile already exists (200 OK):
{ "created": false, "path": "/etc/grafanabridge/config.ini", "reason": "already exists" }Note: if the bridge was started with
-F /path/to/config.inithat file already exists by definition, so this endpoint is only needed when no custom file is present at all.
curl -s -u scale_admin:MyPassword \
http://scale-bridge.example.com:4242/config/validateValid configuration (200 OK):
{ "valid": true }Invalid configuration (e.g. missing API key, 200 OK):
{
"valid": false,
"errors": ["Missing mandatory ApiKey settings, quitting"]
}POST a JSON object with the keys you intend to change. The bridge merges the values into a copy of the live configuration and validates the result. Nothing is applied or written to disk.
curl -s -u scale_admin:MyPassword \
-X POST \
-H "Content-Type: application/json" \
-d '{"protocol": "https", "tlsKeyPath": "/etc/bridge_ssl/certs", "tlsKeyFile": "privkey.pem", "tlsCertFile": "cert.pem"}' \
http://scale-bridge.example.com:4242/config/validateChange would be valid (200 OK):
{
"valid": true,
"candidate": {
"protocol": "https",
"tlsKeyPath": "/etc/bridge_ssl/certs",
"tlsKeyFile": "privkey.pem",
"tlsCertFile": "cert.pem",
"..." : "..."
}
}Change would be invalid (200 OK):
{
"valid": false,
"errors": ["Missing certificates in the specified keyPath directory, quitting"],
"candidate": { "..." : "..." }
}The candidate field always reflects the full merged configuration that was
checked (secret keys password and apiKeyValue are omitted).
The --validate flag accepts an optional MODE argument:
| Invocation | Behaviour |
|---|---|
--validate or --validate config
|
Validate and exit. Code 0 = valid, 1 = invalid. |
--validate start |
Validate and, if valid, continue starting the bridge. |
Suitable for deployment scripts or systemd ExecStartPre= directives.
# Bare flag (same as --validate config):
python zimonGrafanaIntf.py -F /etc/grafanabridge/config.ini --validate
# Explicit mode:
python zimonGrafanaIntf.py -F /etc/grafanabridge/config.ini --validate configValid (exits with code 0):
Config valid.
Invalid (exits with code 1):
Missing mandatory ApiKey settings, quitting
Use in a systemd unit to block startup on an invalid config:
ExecStartPre=python zimonGrafanaIntf.py -F /etc/grafanabridge/config.ini --validate config
ExecStart=python zimonGrafanaIntf.py -F /etc/grafanabridge/config.iniValidates the configuration and, if valid, continues to start the bridge normally — no separate invocation needed.
python zimonGrafanaIntf.py -F /etc/grafanabridge/config.ini --validate startValid — bridge starts:
Config valid. Starting bridge...
Invalid — bridge does not start (exits with code 1):
Missing mandatory ApiKey settings, quitting
The /config endpoint inherits the global HTTP Basic Authentication already
configured for the bridge. All requests must include an Authorization
header.
To skip certificate verification when the bridge uses HTTPS:
curl -k -u scale_admin:MyPassword https://scale-bridge.example.com:8443/configWhen the bridge was started with a custom config file (-F /path/to/config.ini)
or the default RPM override file /etc/grafanabridge/config.ini exists, every
successful write is flushed back to that file. The bundled template
config.ini that ships with the bridge is never modified.
If no writable target file is available the update is still applied in-memory
and "persisted": false is returned. The change will be lost when the bridge
is restarted.