Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@ and this project adheres to
## [unreleased]

### Added
- docs: Mention support of RHEL 10, Fedora 43, SLES and openSUSE 15 and 16.
- cli: Environment variables `RACKSDB_DB`, `RACKSDB_SCHEMA`,
`RACKSDB_EXTENSIONS`, and to set default schema, extensions, and database
paths when the matching `racksdb` / `racksdb-web` option is omitted (#149).
- web: Support the same environment variables as CLI.
- docs:
- Mention support of RHEL 10, Fedora 43, SLES and openSUSE 15 and 16.
- Mention support of `RACKSDB_DB`, `RACKSDB_SCHEMA` and `RACKSDB_EXTENSIONS`
environment variables in `racksdb` and `racksdb-web` manpages.

### Fixed
- docs: Add missing system dependency `libpango1.0-dev` to install from sources,
reported by @astappiev (#148).

### Removed
- docs:
- Drop support of Fedora 41.
- Add missing system dependency `libpango1.0-dev` to install from sources,
reported by @astappiev (#148).
- docs: Drop support of Fedora 41.

## [0.6.0] - 2025-10-16

Expand Down
25 changes: 22 additions & 3 deletions docs/modules/usage/pages/racksdb-web.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@ endif::[]
Path to database. Both files and directories paths are accepted. If the path
is a directory, all YAML files in this directory are loaded, recursively. If
the path does not exist, an error is reported. Default value is
[.path]#`/var/lib/racksdb/`# directory.
[.path]#`/var/lib/racksdb/`# directory or environment variable
[.cli-opt]#*RACKSDB_DB*# if set.

[.cli-opt]#*-s, --schema*=#[.cli-optval]##_SCHEMA_##::
Path to RacksDB schema YAML file. If the file does not exist, an error is
reported. Default value is [.path]#`/usr/share/racksdb/schemas/racksdb.yml`#.
reported. Default value is [.path]#`/usr/share/racksdb/schemas/racksdb.yml`#
or environment variable [.cli-opt]#*RACKSDB_SCHEMA*# if set.

[.cli-opt]#*-e, --ext*=#[.cli-optval]##_EXT_##::
Path to optional RacksDB schema extensions. If the file does not exist,
it is silently ignored by RacksDB. Default value is
[.path]#`/etc/racksdb/extensions.yml`#.
[.path]#`/etc/racksdb/extensions.yml`# or environment variable
[.cli-opt]#*RACKSDB_EXTENSIONS*# if set.

[.cli-opt]#*--host*=#[.cli-optval]##_HOST_##::
The hostname to listen for incoming requests. Set to `0.0.0.0` to listen on
Expand All @@ -61,6 +64,22 @@ endif::[]
without value, default UI path [.path]#`/usr/share/racksdb/frontend`# is used.
When a path is provided, `racksdb-web` loads UI files from this path.

== Environment variables

When a command-line option above is omitted, `racksdb-web` uses the following
environment variables if they are set to a non-empty value:

[.cli-opt]#*RACKSDB_SCHEMA*#::
Path to the RacksDB schema YAML file.

[.cli-opt]#*RACKSDB_EXTENSIONS*#::
Path to optional schema extensions.

[.cli-opt]#*RACKSDB_DB*#::
Path to the database.

Command-line options always override environment variables.

== Exit status

*0*::
Expand Down
25 changes: 22 additions & 3 deletions docs/modules/usage/pages/racksdb.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,19 @@ endif::[]
Path to database. Both files and directories paths are accepted. If the path
is a directory, all YAML files in this directory are loaded, recursively. If
the path does not exist, an error is reported. Default value is
[.path]#`/var/lib/racksdb/`# directory.
[.path]#`/var/lib/racksdb/`# directory or environment variable
[.cli-opt]#*RACKSDB_DB*# if set.

[.cli-opt]#*-s, --schema*=#[.cli-optval]##_SCHEMA_##::
Path to RacksDB schema YAML file. If the file does not exist, an error is
reported. Default value is [.path]#`/usr/share/racksdb/schemas/racksdb.yml`#.
reported. Default value is [.path]#`/usr/share/racksdb/schemas/racksdb.yml`#
or environment variable [.cli-opt]#*RACKSDB_SCHEMA*# if set.

[.cli-opt]#*-e, --ext*=#[.cli-optval]##_EXT_##::
Path to optional RacksDB schema extensions. If the file does not exist,
it is silently ignored by RacksDB. Default value is
[.path]#`/etc/racksdb/extensions.yml`#.
[.path]#`/etc/racksdb/extensions.yml`# or environment variable
[.cli-opt]#*RACKSDB_EXTENSIONS*# if set.

== Commands

Expand Down Expand Up @@ -373,6 +376,22 @@ Generate graphical representation of _noisy_ datacenter room and dump
coordinates of racks in JSON format in a file named `noisy-coordinates.json`.
====

== Environment variables

When a command-line option above is omitted, `racksdb` uses the following
environment variables if they are set to a non-empty value:

[.cli-opt]#*RACKSDB_SCHEMA*#::
Path to the RacksDB schema YAML file.

[.cli-opt]#*RACKSDB_EXTENSIONS*#::
Path to optional schema extensions.

[.cli-opt]#*RACKSDB_DB*#::
Path to the database.

Command-line options always override environment variables.

== Exit status

*0*::
Expand Down
25 changes: 25 additions & 0 deletions racksdb/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2025 Rackslab
#
# This file is part of RacksDB.
#
# SPDX-License-Identifier: MIT

"""Environment variable names and helpers for RacksDB CLI entry points."""

import os
from pathlib import Path
import typing as t


class RacksDBEnv:
SCHEMA = "RACKSDB_SCHEMA"
EXTENSIONS = "RACKSDB_EXTENSIONS"
DB = "RACKSDB_DB"


def env_or_default(env_key: str, fallback: t.Union[str, Path]) -> Path:
"""Path from ``os.environ[env_key]`` if set and non-empty, else *fallback*."""
val = os.environ.get(env_key)
if val:
return Path(val)
return Path(fallback)
7 changes: 4 additions & 3 deletions racksdb/exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
)
from .generic.dumpers import DBDumperFactory, SchemaDumperFactory
from . import RacksDB
from .env import RacksDBEnv, env_or_default
from .drawers import InfrastructureDrawer, AxonometricInfrastructureDrawer, RoomDrawer
from .drawers.parameters import DrawingParameters
from .errors import RacksDBError
Expand Down Expand Up @@ -57,21 +58,21 @@ def __init__(self, cmd_args: t.Optional[t.List[str]] = None):
"-s",
"--schema",
help="Schema to load (default: %(default)s)",
default=RacksDB.DEFAULT_SCHEMA,
default=env_or_default(RacksDBEnv.SCHEMA, RacksDB.DEFAULT_SCHEMA),
type=Path,
)
parser.add_argument(
"-e",
"--ext",
help="Path to extensions of schema (default: %(default)s)",
default=RacksDB.DEFAULT_EXT,
default=env_or_default(RacksDBEnv.EXTENSIONS, RacksDB.DEFAULT_EXT),
type=Path,
)
parser.add_argument(
"-b",
"--db",
help="Database to load (default: %(default)s)",
default=RacksDB.DEFAULT_DB,
default=env_or_default(RacksDBEnv.DB, RacksDB.DEFAULT_DB),
type=Path,
)

Expand Down
39 changes: 39 additions & 0 deletions racksdb/tests/test_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (c) 2025 Rackslab
#
# This file is part of RacksDB.
#
# SPDX-License-Identifier: MIT

import os
import unittest
from pathlib import Path
from unittest import mock

from racksdb.env import env_or_default


class TestEnvOrDefault(unittest.TestCase):
def test_uses_fallback_when_key_missing(self):
key = "RACKSDB_TEST_ENV_OR_DEFAULT_MISSING"
self.assertNotIn(key, os.environ)
p = env_or_default(key, "/tmp/fallback")
self.assertEqual(p, Path("/tmp/fallback"))

def test_uses_fallback_when_empty_string(self):
key = "RACKSDB_TEST_ENV_OR_DEFAULT_EMPTY"
with mock.patch.dict(os.environ, {key: ""}, clear=False):
p = env_or_default(key, "/tmp/fallback")
self.assertEqual(p, Path("/tmp/fallback"))

def test_uses_environment_when_set(self):
key = "RACKSDB_TEST_ENV_OR_DEFAULT_SET"
with mock.patch.dict(os.environ, {key: "/from/env"}, clear=False):
p = env_or_default(key, "/tmp/fallback")
self.assertEqual(p, Path("/from/env"))

def test_fallback_accepts_path(self):
key = "RACKSDB_TEST_ENV_OR_DEFAULT_PATH_FALLBACK"
fb = Path("/tmp/fallback_path")
with mock.patch.dict(os.environ, {key: ""}, clear=False):
p = env_or_default(key, fb)
self.assertEqual(p, fb)
77 changes: 77 additions & 0 deletions racksdb/tests/test_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# SPDX-License-Identifier: MIT

import unittest
from unittest import mock
import io
import tempfile
Expand All @@ -15,6 +16,7 @@
import yaml

from racksdb import RacksDB
from racksdb.env import RacksDBEnv
from racksdb.exec import RacksDBExec
from racksdb.version import get_version

Expand Down Expand Up @@ -695,3 +697,78 @@ def test_autopager_used_in_views(self):
RacksDBExec(CMD_BASE_ARGS + ["datacenters"])
autopager.assert_called_once()
autopager.return_value.__enter__.assert_called_once()


class TestRacksDBExecLoadArguments(unittest.TestCase):
"""``RacksDB.load()`` receives paths from argparse defaults (env + fallbacks)."""

@staticmethod
def _mock_db_for_dump():
mdb = mock.MagicMock()
mdb._loader.content = {}
return mdb

def test_load_args_use_class_defaults_when_env_keys_empty(self):
with mock.patch.dict(
os.environ,
{
RacksDBEnv.SCHEMA: "",
RacksDBEnv.EXTENSIONS: "",
RacksDBEnv.DB: "",
},
clear=False,
):
with mock.patch("racksdb.exec.RacksDB.load") as load_mock:
load_mock.return_value = self._mock_db_for_dump()
with mock.patch("sys.stdout", new=io.StringIO()):
RacksDBExec(["dump"])
load_mock.assert_called_once_with(
Path(RacksDB.DEFAULT_SCHEMA),
Path(RacksDB.DEFAULT_EXT),
Path(RacksDB.DEFAULT_DB),
)

def test_load_args_use_environment_when_set(self):
env = {
RacksDBEnv.SCHEMA: "/env/schema.yml",
RacksDBEnv.EXTENSIONS: "/env/extensions.yml",
RacksDBEnv.DB: "/env/db",
}
with mock.patch.dict(os.environ, env, clear=False):
with mock.patch("racksdb.exec.RacksDB.load") as load_mock:
load_mock.return_value = self._mock_db_for_dump()
with mock.patch("sys.stdout", new=io.StringIO()):
RacksDBExec(["dump"])
load_mock.assert_called_once_with(
Path("/env/schema.yml"),
Path("/env/extensions.yml"),
Path("/env/db"),
)

def test_load_args_prefer_cli_over_environment(self):
with mock.patch.dict(
os.environ,
{
RacksDBEnv.SCHEMA: "/wrong/schema.yml",
RacksDBEnv.EXTENSIONS: "",
RacksDBEnv.DB: "/wrong/db",
},
clear=False,
):
with mock.patch("racksdb.exec.RacksDB.load") as load_mock:
load_mock.return_value = self._mock_db_for_dump()
with mock.patch("sys.stdout", new=io.StringIO()):
RacksDBExec(
[
"--schema",
"/cli/schema.yml",
"--db",
"/cli/db",
"dump",
]
)
load_mock.assert_called_once_with(
Path("/cli/schema.yml"),
Path(RacksDB.DEFAULT_EXT),
Path("/cli/db"),
)
Loading
Loading