Skip to content

Commit 3b66fcb

Browse files
vojtechtrefnyrichm
authored andcommitted
tests: Add a custom script for getting info about Stratis pools
There are multiple versions of Stratis in systems we support and it is hard to get all the information to verify test results from the Stratis cmdline utility in a consistent way. This adds a small Python script that uses Stratis DBus API to get the information we need for the tests.
1 parent b3ab7f4 commit 3b66fcb

2 files changed

Lines changed: 169 additions & 12 deletions

File tree

tests/scripts/stratis_pool_info.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/env python
2+
3+
# Helper script for gathering information about stratis pools using stratis DBus API.
4+
5+
# The script is meant to be a supporting tool for the storage role tests
6+
7+
import sys
8+
from collections import namedtuple
9+
10+
import json
11+
12+
import gi # pylint: disable=import-error
13+
gi.require_version("GLib", "2.0")
14+
gi.require_version("Gio", "2.0")
15+
16+
from gi.repository import GLib, Gio # pylint: disable=import-error
17+
18+
STRATIS_SERVICE = "org.storage.stratis3"
19+
STRATIS_PATH = "/org/storage/stratis3"
20+
STRATIS_POOL_INTF = STRATIS_SERVICE + ".pool.r0"
21+
22+
# Code for working with DBus, taken from blivet/safe_dbus.py
23+
DBUS_PROPS_IFACE = "org.freedesktop.DBus.Properties"
24+
DBUS_INTRO_IFACE = "org.freedesktop.DBus.Introspectable"
25+
26+
27+
class SafeDBusError(Exception):
28+
"""Class for exceptions defined in this module."""
29+
30+
31+
class DBusCallError(SafeDBusError):
32+
"""Class for the errors related to calling methods over DBus."""
33+
34+
35+
class DBusPropertyError(DBusCallError):
36+
"""Class for the errors related to getting property values over DBus."""
37+
38+
39+
def get_new_system_connection():
40+
"""Return a new connection to the system bus."""
41+
42+
return Gio.DBusConnection.new_for_address_sync(
43+
Gio.dbus_address_get_for_bus_sync(Gio.BusType.SYSTEM, None),
44+
Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT
45+
| Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION,
46+
None, None)
47+
48+
49+
def call_sync(service, obj_path, iface, method, args, connection=None, fds=None):
50+
if not connection:
51+
try:
52+
connection = get_new_system_connection()
53+
except GLib.GError as gerr:
54+
raise DBusCallError("Unable to connect to system bus: %s" % gerr) from gerr
55+
56+
if connection.is_closed():
57+
raise DBusCallError("Connection is closed")
58+
59+
try:
60+
ret = connection.call_with_unix_fd_list_sync(service, obj_path, iface, method,
61+
args, None, Gio.DBusCallFlags.NONE,
62+
-1, fds, None)
63+
except GLib.GError as gerr:
64+
msg = "Failed to call %s method on %s with %s arguments: %s" % \
65+
(method, obj_path, args, gerr.message) # pylint: disable=no-member
66+
raise DBusCallError(msg) from gerr
67+
68+
if ret is None:
69+
msg = "No return from %s method on %s with %s arguments" % (method, obj_path,
70+
args)
71+
raise DBusCallError(msg)
72+
73+
return ret[0].unpack()
74+
75+
76+
def get_properties_sync(service, obj_path, iface, connection=None):
77+
args = GLib.Variant('(s)', (iface,))
78+
ret = call_sync(service, obj_path, DBUS_PROPS_IFACE, "GetAll", args,
79+
connection)
80+
return ret
81+
82+
83+
# Extracting and printing Stratis pool information
84+
StratisPoolInfo = namedtuple("StratisPoolInfo", ["name", "encrypted", "key_desc",
85+
"clevis_pin", "clevis_args"])
86+
87+
88+
def _print_pool_info_json(pool_info):
89+
pi_dict = pool_info._asdict()
90+
pi_json = json.dumps(pi_dict)
91+
print(pi_json)
92+
93+
94+
def _get_pool_info(pool_path):
95+
try:
96+
properties = get_properties_sync(STRATIS_SERVICE,
97+
pool_path,
98+
STRATIS_POOL_INTF)[0]
99+
except DBusPropertyError:
100+
return None
101+
102+
if not properties:
103+
return None
104+
105+
description = properties.get("KeyDescription", None)
106+
if not description or not description[0] or not description[1][0]:
107+
key_desc = None
108+
else:
109+
key_desc = description[1][1]
110+
111+
clevis_info = properties.get("ClevisInfo", None)
112+
if not clevis_info or not clevis_info[0] or not clevis_info[1][0]:
113+
clevis = None
114+
else:
115+
clevis = clevis_info[1][1]
116+
117+
if clevis:
118+
clevis_pin = clevis[0]
119+
clevis_args = json.loads(clevis[1])
120+
else:
121+
clevis_pin = None
122+
clevis_args = {}
123+
124+
return StratisPoolInfo(name=properties["Name"],
125+
encrypted=properties["Encrypted"],
126+
key_desc=key_desc,
127+
clevis_pin=clevis_pin,
128+
clevis_args=clevis_args)
129+
130+
131+
def main(pool_name):
132+
objects = call_sync(STRATIS_SERVICE,
133+
STRATIS_PATH,
134+
"org.freedesktop.DBus.ObjectManager",
135+
"GetManagedObjects",
136+
None)[0]
137+
138+
for path, interfaces in objects.items():
139+
if STRATIS_POOL_INTF in interfaces.keys():
140+
pool_info = _get_pool_info(path)
141+
if pool_info and pool_info.name == pool_name:
142+
_print_pool_info_json(pool_info)
143+
return True
144+
145+
print(json.dumps(None))
146+
return True
147+
148+
149+
if __name__ == "__main__":
150+
if len(sys.argv) != 2:
151+
print("Usage: python %s <pool name>" % sys.argv[0], file=sys.stderr)
152+
sys.exit(1)
153+
154+
succ = main(sys.argv[1])
155+
sys.exit(0) if succ else sys.exit(1)

tests/verify-pool-stratis.yml

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,33 @@
33
- name: Check Stratis options
44
when: storage_test_pool.type == 'stratis'
55
block:
6-
- name: Run 'stratis report'
7-
command: stratis report
6+
- name: Get stratis pool information
7+
ansible.builtin.script: >-
8+
scripts/stratis_pool_info.py
9+
"{{ storage_test_pool.name }}"
10+
args:
11+
executable: "{{ ansible_python.executable }}"
812
register: storage_test_stratis_report
913
changed_when: false
1014

15+
- name: Print script output
16+
debug:
17+
msg: "{{ storage_test_stratis_report.stdout }}"
18+
1119
- name: Get information about Stratis
1220
set_fact:
1321
_stratis_pool_info: "{{ storage_test_stratis_report.stdout | from_json }}"
1422

1523
- name: Verify that the pools was created
1624
assert:
17-
that: _stratis_pool_info.pools | length == 1 and
18-
_stratis_pool_info.pools[0].name == storage_test_pool.name
25+
that: _stratis_pool_info.name == storage_test_pool.name
1926
msg: >-
2027
Stratis pool '{{ storage_test_pool.name }}' not found
2128
when: storage_test_pool.state == 'present'
2229

23-
# Stratis internally uses LUKS so verify-pool-member-encryption will also
24-
# cover this we just need to make sure this is encrypted Stratis pool
25-
# and not Stratis on top of "normal LUKS
2630
- name: Verify that encryption is correctly set
2731
assert:
28-
that: storage_test_pool.name in
29-
_stratis_pool_info.pools[0]['blockdevs']['datadevs'][0]['key_description']
32+
that: storage_test_pool.name in _stratis_pool_info.key_desc
3033
msg: >-
3134
Stratis pool '{{ storage_test_pool.name }}' is not encrypted
3235
when:
@@ -36,9 +39,8 @@
3639
- name: Verify that Clevis/Tang encryption is correctly set
3740
assert:
3841
that:
39-
_stratis_pool_info.pools[0]['blockdevs']['datadevs'][0]['clevis_pin'] == 'tang' and
40-
_stratis_pool_info.pools[0]['blockdevs']['datadevs'][0]['clevis_config']['url'] ==
41-
storage_test_pool.encryption_tang_url
42+
_stratis_pool_info.clevis_pin == 'tang' and
43+
_stratis_pool_info.clevis_args['url'] == storage_test_pool.encryption_tang_url
4244
msg: >-
4345
Stratis pool '{{ storage_test_pool.name }}' Clevis is not correctly configured
4446
when:

0 commit comments

Comments
 (0)