Skip to content

Commit

Permalink
Merge pull request #3556 from mulkieran/issue_project_650_b
Browse files Browse the repository at this point in the history
Add script to get Stratis info from devicemapper path
  • Loading branch information
mulkieran authored Feb 22, 2024
2 parents cddec4d + 359bca7 commit c4c1545
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 1 deletion.
1 change: 1 addition & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ make fmt-ci &&
make clippy &&
make yamllint &&
make tmtlint &&
make pylint &&
make check-typos || exit 1

export PYTHONPATH=$PWD/tests/client-dbus/src
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
- name: Install dependencies for Fedora
run: >
dnf install -y
black
clang
curl
cryptsetup-devel
Expand All @@ -62,6 +63,7 @@ jobs:
libblkid-devel
make
ncurses
python3-isort
systemd-devel
- uses: dtolnay/rust-toolchain@master
with:
Expand Down Expand Up @@ -235,6 +237,21 @@ jobs:
- name: Run shell check
run: make -f Makefile fmt-shell-ci

pylint:
runs-on: ubuntu-22.04
container:
image: fedora:39 # CURRENT DEVELOPMENT ENVIRONMENT
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: >
dnf install -y
make
pylint
python3-dbus
- name: Run pylint
run: make -f Makefile pylint

python-based-tests:
runs-on: ubuntu-22.04
container:
Expand Down
14 changes: 14 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88

sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCAL,LOCALFOLDER

import_heading_future=isort: FUTURE
import_heading_stdlib=isort: STDLIB
import_heading_thirdparty=isort: THIRDPARTY
import_heading_firstparty=isort: FIRSTPARTY
import_heading_local=isort: LOCAL
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,14 @@ check-typos:
## Run cargo fmt
fmt: fmt-macros
cargo fmt
isort ./src/bin/utils/stratis-decode-dm
black ./src/bin/utils/stratis-decode-dm

## Run cargo fmt for CI jobs
fmt-ci: fmt-macros-ci
cargo fmt -- --check
isort --diff --check-only ./src/bin/utils/stratis-decode-dm
black ./src/bin/utils/stratis-decode-dm --check

## Run cargo fmt for stratisd_proc_macros
fmt-macros:
Expand Down Expand Up @@ -333,6 +337,11 @@ install-systemd-cfg:
sed 's|@LIBEXECDIR@|$(LIBEXECDIR)|' systemd/stratisd-min-postinitrd.service.in > $(DESTDIR)$(UNITDIR)/stratisd-min-postinitrd.service
sed 's|@UNITEXECDIR@|$(UNITEXECDIR)|' systemd/[email protected] > $(DESTDIR)$(UNITDIR)/[email protected]

## Install scripts
install-scripts:
mkdir -p $(DESTDIR)$(BINDIR)
$(INSTALL) -Dpm0755 -t $(DESTDIR)$(BINDIR) src/bin/utils/stratis-decode-dm

## Install binaries
install-binaries:
mkdir -p $(DESTDIR)$(BINDIR)
Expand Down Expand Up @@ -365,7 +374,7 @@ install-daemons:
$(INSTALL) -Dpm0755 -t $(DESTDIR)$(LIBEXECDIR) target/$(PROFILEDIR)/stratisd-min

## Install all stratisd files
install: install-udev-cfg install-man-cfg install-dbus-cfg install-dracut-cfg install-systemd-cfg install-binaries install-udev-binaries install-fstab-script install-daemons
install: install-udev-cfg install-man-cfg install-dbus-cfg install-dracut-cfg install-systemd-cfg install-scripts install-binaries install-udev-binaries install-fstab-script install-daemons

## Build all Rust artifacts
build-all-rust: build build-min build-utils build-udev-utils stratisd-tools
Expand Down Expand Up @@ -504,6 +513,10 @@ clippy-no-ipc:
clippy: clippy-macros clippy-min clippy-udev-utils clippy-no-ipc clippy-utils
RUSTFLAGS="${DENY}" cargo clippy ${CLIPPY_OPTS} -- ${CLIPPY_DENY} ${CLIPPY_PEDANTIC} ${CLIPPY_PEDANTIC_USELESS}

## Lint Python parts of the source code
pylint:
pylint --disable=invalid-name ./src/bin/utils/stratis-decode-dm

.PHONY:
audit
audit-all-rust
Expand Down Expand Up @@ -541,10 +554,12 @@ clippy: clippy-macros clippy-min clippy-udev-utils clippy-no-ipc clippy-utils
install-dracut-cfg
install-fstab-script
install-man-cfg
install-scripts
install-systemd-cfg
install-udev-binaries
install-udev-cfg
license
pylint
test
test-valgrind
test-loop
Expand Down
203 changes: 203 additions & 0 deletions src/bin/utils/stratis-decode-dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/usr/bin/env python3

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Script to map from devicemapper name to Stratis values
"""

# isort: STDLIB
import argparse
import pathlib
from enum import Enum
from uuid import UUID

# isort: THIRDPARTY
import dbus

_REVISION_NUMBER = 7
_REVISION = f"r{_REVISION_NUMBER}"
_BUS_NAME = "org.storage.stratis3"
_TOP_OBJECT = "/org/storage/stratis3"
_OBJECT_MANAGER = "org.freedesktop.DBus.ObjectManager"
_TIMEOUT = 120000
_POOL_IFACE = f"{_BUS_NAME}.pool.{_REVISION}"
_FS_IFACE = f"{_BUS_NAME}.filesystem.{_REVISION}"
_DEV_PATH = "/dev/stratis"


class OutputMode(Enum):
"""
Output mode choices.
"""

FILESYSTEM_NAME = "filesystem-name"
POOL_NAME = "pool-name"
SYMLINK = "symlink"

def __str__(self):
return self.value


def _parse_dm_name(dmname):
"""
Parse a Stratis filesystem devicemapper name.
:param str dmname: the devicemapper name of the filesystem device
:returns: the pool and filesystem UUID
:rtype: UUID * UUID
"""
try:
(stratis, format_version, pool_uuid, thin, fs, filesystem_uuid) = dmname.split(
"-"
)
except ValueError as err:
raise RuntimeError(
f"error parsing Stratis filesystem devicemapper name {dmname}"
) from err

if stratis != "stratis" or format_version != "1" or thin != "thin" or fs != "fs":
raise RuntimeError(
f"error parsing Stratis filesystem devicemapper name {dmname}"
)

return (UUID(pool_uuid), UUID(filesystem_uuid))


def _extract_dm_name(dmpath):
"""
Extract the devicemapper name from the devicemapper path.
:param Path dmpath: The devicemapper path.
:returns: devicemapper name
:rtype: str
"""

assert dmpath.is_absolute(), "parser ensures absolute path"

try:
(_, dev, mapper, name) = dmpath.parts
except ValueError as err:
raise RuntimeError(
f"error decomposing Stratis filesystem devicemapper path: {dmpath}"
) from err

if dev != "dev" or mapper != "mapper":
raise RuntimeError(
f"error decomposing Stratis filesystem devicemapper path: {dmpath}"
)

return name


def _get_managed_objects():
"""
Get managed objects for stratis
:return: A dict, Keys are object paths with dicts containing interface
names mapped to property dicts.
Property dicts map names to values.
"""
object_manager = dbus.Interface(
dbus.SystemBus().get_object(_BUS_NAME, _TOP_OBJECT),
_OBJECT_MANAGER,
)
return object_manager.GetManagedObjects(timeout=_TIMEOUT)


def _get_parser():
"""
Build a parser for this script.
"""

def _abs_path(path):
parsed_path = pathlib.Path(path)
if not parsed_path.is_absolute():
raise argparse.ArgumentTypeError(
f"{path} must be specified as an absolute path"
)

return parsed_path

parser = argparse.ArgumentParser(
description="Utility that maps from Stratis filesystem devicemapper path to a Stratis value"
)
parser.add_argument(
"path",
help="The absolute path of the devicemapper device ('/dev/mapper/<devicemapper-name>')",
metavar="PATH",
type=_abs_path,
)
parser.add_argument(
"--output",
choices=list(OutputMode),
help="Stratis value to print",
type=OutputMode,
required=True,
)
return parser


def main():
"""
The main method.
"""
parser = _get_parser()
namespace = parser.parse_args()

dm_name = _extract_dm_name(namespace.path)

(pool_uuid, filesystem_uuid) = _parse_dm_name(dm_name)

managed_objects = _get_managed_objects()

(pool_uuid_str, filesystem_uuid_str) = (pool_uuid.hex, filesystem_uuid.hex)

pool_name = next(
(
obj_data[_POOL_IFACE]["Name"]
for obj_data in managed_objects.values()
if _POOL_IFACE in obj_data
and obj_data[_POOL_IFACE]["Uuid"] == pool_uuid_str
),
None,
)

filesystem_name = next(
(
obj_data[_FS_IFACE]["Name"]
for obj_data in managed_objects.values()
if _FS_IFACE in obj_data
and obj_data[_FS_IFACE]["Uuid"] == filesystem_uuid_str
),
None,
)

if namespace.output is OutputMode.SYMLINK:
if pool_name is None:
raise RuntimeError(
"Pool name could not be found; can not synthesize Stratis filesystem symlink"
)
if filesystem_name is None:
raise RuntimeError(
"Filesystem name could not be found; can not synthesize Stratis filesystem symlink"
)
print(pathlib.Path(_DEV_PATH, pool_name, filesystem_name))

elif namespace.output is OutputMode.FILESYSTEM_NAME:
if filesystem_name is None:
raise RuntimeError("Filesystem name could not be found")
print(filesystem_name)

elif namespace.output is OutputMode.POOL_NAME:
if filesystem_name is None:
raise RuntimeError("Pool name could not be found")
print(pool_name)

else:
assert False, "unreachable"


if __name__ == "__main__":
main()

0 comments on commit c4c1545

Please sign in to comment.