Skip to content

Commit 289f5e0

Browse files
ArtiomDivakYarboa
authored andcommitted
Update qmctl packaging and tests to use installed CLI
- Add Python module to qm-ctl package (qmctl.py, __init__.py) - Update all qmctl test scripts to use installed 'qmctl' command - Fixes ModuleNotFoundError when using qmctl from RPM installation closes #939 Signed-off-by: Artiom Divak <adivak@redhat.com>
1 parent 29830da commit 289f5e0

9 files changed

Lines changed: 81 additions & 82 deletions

File tree

rpm/qm.spec

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ BuildRequires: pkgconfig(systemd)
6262
BuildRequires: selinux-policy >= %_selinux_policy_version
6363
BuildRequires: selinux-policy-devel >= %_selinux_policy_version
6464
BuildRequires: bluechi-selinux
65+
BuildRequires: python3-devel
6566

6667
Requires: parted
6768
Requires: containers-common
@@ -117,6 +118,11 @@ sed -i 's|^/run/|/var/run/|' qm.fc
117118
# Create the directory for drop-in configurations
118119
install -d %{buildroot}%{_sysconfdir}/containers/containers.conf.d
119120

121+
# Install Python module for qmctl
122+
install -d %{buildroot}%{python3_sitelib}/qmctl
123+
install -m 0644 tools/qmctl/__init__.py %{buildroot}%{python3_sitelib}/qmctl/
124+
install -m 0644 tools/qmctl/qmctl.py %{buildroot}%{python3_sitelib}/qmctl/
125+
120126
# install policy modules
121127
%_format MODULES $x.pp.bz2
122128
%{__make} DESTDIR=%{buildroot} DATADIR=%{_datadir} install
@@ -181,6 +187,7 @@ fi
181187
%license LICENSE
182188
%{_bindir}/qmctl
183189
%{_mandir}/man1/qmctl.*
190+
%{python3_sitelib}/qmctl/
184191

185192
%changelog
186193
%if %{defined autochangelog}

tests/qmctl-test/scripts/qmctl_cp_bidirectional.sh

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ set -e
66
# shellcheck disable=SC1091
77
. ../e2e/lib/utils
88

9-
QMCTL_SCRIPT="../../tools/qmctl/qmctl"
10-
119
info_message "Starting comprehensive qmctl copy tests..."
1210

1311
# Check if container is available
14-
if ! python3 "$QMCTL_SCRIPT" exec echo "container_check" > /dev/null 2>&1; then
12+
if ! qmctl exec echo "container_check" > /dev/null 2>&1; then
1513
fail_message "QM container is not available - copy tests cannot run"
1614
echo "Please ensure QM container is running with: sudo systemctl start qm"
1715
exit 1
@@ -21,12 +19,12 @@ info_message "QM container is available - running comprehensive copy tests"
2119

2220
# Test 0: Basic copy test (from original qmctl_cp.sh)
2321
info_message "Testing: Basic copy test (host to container)"
24-
python3 "$QMCTL_SCRIPT" cp ./files/file-to-copy.txt qm:/tmp
22+
qmctl cp ./files/file-to-copy.txt qm:/tmp
2523

2624
expected_output_file="./files/file-to-check-cp.txt"
2725
actual_output_temp_file=$(mktemp)
2826

29-
python3 "$QMCTL_SCRIPT" exec cat /tmp/file-to-copy.txt > "$actual_output_temp_file"
27+
qmctl exec cat /tmp/file-to-copy.txt > "$actual_output_temp_file"
3028

3129
if diff "$actual_output_temp_file" "$expected_output_file" > /dev/null; then
3230
pass_message "Basic copy - File content matches expected"
@@ -39,7 +37,7 @@ fi
3937
rm -f "$actual_output_temp_file"
4038

4139
# Clean up the basic test file
42-
python3 "$QMCTL_SCRIPT" exec bash -c "rm -f /tmp/file-to-copy.txt && echo 'cleaned up basic test file'" >/dev/null 2>&1 || true
40+
qmctl exec bash -c "rm -f /tmp/file-to-copy.txt && echo 'cleaned up basic test file'" >/dev/null 2>&1 || true
4341

4442
# Function to create a unique test file with specific content
4543
create_test_file() {
@@ -73,7 +71,7 @@ verify_copy_content() {
7371
if [[ "$actual_dst_path" == qm:* ]]; then
7472
# Destination is in container - use exec to read content
7573
local container_path="${actual_dst_path#qm:}"
76-
if actual_content=$(python3 "$QMCTL_SCRIPT" exec cat "$container_path" 2>/dev/null); then
74+
if actual_content=$(qmctl exec cat "$container_path" 2>/dev/null); then
7775
# Success - continue with verification
7876
:
7977
else
@@ -115,7 +113,7 @@ test_copy_with_verification() {
115113

116114
# Run the copy command using the general run_test function
117115
if run_test "$test_name" 0 "none" "" \
118-
python3 "$QMCTL_SCRIPT" cp "$src_path" "$dst_path"; then
116+
qmctl cp "$src_path" "$dst_path"; then
119117

120118
# If copy succeeded, verify content
121119
if verify_copy_content "$test_name" "$dst_path" "$expected_content" "$src_path"; then
@@ -127,7 +125,7 @@ test_copy_with_verification() {
127125
if [ -n "$cleanup_paths" ]; then
128126
for cleanup_path in $cleanup_paths; do
129127
local container_cleanup_path="${cleanup_path#qm:}"
130-
python3 "$QMCTL_SCRIPT" exec bash -c "rm -f '$container_cleanup_path' && echo 'cleaned up $container_cleanup_path'" >/dev/null 2>&1 || true
128+
qmctl exec bash -c "rm -f '$container_cleanup_path' && echo 'cleaned up $container_cleanup_path'" >/dev/null 2>&1 || true
131129
done
132130
fi
133131
}
@@ -148,7 +146,7 @@ test_copy_with_verification \
148146
test_content_2="Bidirectional copy test 2: container to host"
149147

150148
# First, create file in container
151-
python3 "$QMCTL_SCRIPT" exec bash -c "echo '$test_content_2' > /tmp/bidi_test2.txt && echo 'File created in container'"
149+
qmctl exec bash -c "echo '$test_content_2' > /tmp/bidi_test2.txt && echo 'File created in container'"
152150

153151
# Then copy from container to host
154152
test_file_2="/tmp/bidi_test2_result.txt"
@@ -165,7 +163,7 @@ test_file_5=$(mktemp)
165163
create_test_file "$test_file_5" "$test_content_5"
166164

167165
# Ensure target directory exists in container
168-
python3 "$QMCTL_SCRIPT" exec bash -c "mkdir -p /tmp/copy_test_dir && echo 'Directory created'"
166+
qmctl exec bash -c "mkdir -p /tmp/copy_test_dir && echo 'Directory created'"
169167

170168
# When copying to a directory, the file keeps its original name
171169
test_file_5_basename=$(basename "$test_file_5")
@@ -178,14 +176,14 @@ test_copy_with_verification \
178176

179177
# The actual destination file will be at /tmp/copy_test_dir/$test_file_5_basename
180178
# Clean up the file we just copied
181-
python3 "$QMCTL_SCRIPT" exec bash -c "rm -f '/tmp/copy_test_dir/$test_file_5_basename' && echo 'cleaned up test file'" >/dev/null 2>&1 || true
179+
qmctl exec bash -c "rm -f '/tmp/copy_test_dir/$test_file_5_basename' && echo 'cleaned up test file'" >/dev/null 2>&1 || true
182180

183181
# Test 6: Container directory to host
184182
test_content_6="Container directory to host test"
185183

186184
# Create file in container directory
187-
python3 "$QMCTL_SCRIPT" exec bash -c "mkdir -p /tmp/container_source_dir && echo 'Source directory created'"
188-
python3 "$QMCTL_SCRIPT" exec bash -c "echo '$test_content_6' > /tmp/container_source_dir/test_file.txt && echo 'File created in container directory'"
185+
qmctl exec bash -c "mkdir -p /tmp/container_source_dir && echo 'Source directory created'"
186+
qmctl exec bash -c "echo '$test_content_6' > /tmp/container_source_dir/test_file.txt && echo 'File created in container directory'"
189187

190188
# Create target directory on host
191189
mkdir -p /tmp/host_target_dir
@@ -198,7 +196,7 @@ test_copy_with_verification \
198196
"/tmp/host_target_dir/test_file.txt"
199197

200198
# Cleanup test directories
201-
python3 "$QMCTL_SCRIPT" exec bash -c "rm -rf /tmp/copy_test_dir /tmp/container_source_dir && echo 'cleaned up test directories'" >/dev/null 2>&1 || true
199+
qmctl exec bash -c "rm -rf /tmp/copy_test_dir /tmp/container_source_dir && echo 'cleaned up test directories'" >/dev/null 2>&1 || true
202200
rm -rf /tmp/host_target_dir 2>/dev/null || true
203201

204202
# All tests passed

tests/qmctl-test/scripts/qmctl_error_handling.sh

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,88 +6,86 @@ set -e
66
# shellcheck disable=SC1091
77
. ../e2e/lib/utils
88

9-
QMCTL_SCRIPT="../../tools/qmctl/qmctl"
10-
119
info_message "Starting comprehensive qmctl error handling tests..."
1210

1311
# Test 1: No subcommand (should show usage and fail)
1412
run_test "No subcommand" 22 "error_pattern" "requires a subcommand" \
15-
python3 "$QMCTL_SCRIPT"
13+
qmctl
1614

1715
# Test 2: Invalid subcommand
1816
run_test "Invalid subcommand" 2 "error_pattern" "invalid choice" \
19-
python3 "$QMCTL_SCRIPT" invalid_command
17+
qmctl invalid_command
2018

2119
# Test 3: Exec without command
2220
run_test "Exec without command" 22 "error_pattern" "No command provided" \
23-
python3 "$QMCTL_SCRIPT" exec
21+
qmctl exec
2422

2523
# Test 4: Execin without container name
2624
run_test "Execin without container" 1 "error_pattern" "No command provided" \
27-
python3 "$QMCTL_SCRIPT" execin
25+
qmctl execin
2826

2927
# Test 5: Execin with container but no command
3028
run_test "Execin with container but no command" 1 "error_pattern" "No command provided" \
31-
python3 "$QMCTL_SCRIPT" execin some_container
29+
qmctl execin some_container
3230

3331
# Test 6: Copy without any paths
3432
run_test "Copy without paths" 2 "error_pattern" "required" \
35-
python3 "$QMCTL_SCRIPT" cp
33+
qmctl cp
3634

3735
# Test 7: Copy with single path (missing destination)
3836
run_test "Copy with single path" 2 "error_pattern" "required" \
39-
python3 "$QMCTL_SCRIPT" cp /some/path
37+
qmctl cp /some/path
4038

4139
# Test 8: Copy with invalid path format
4240
run_test "Copy with invalid path format" 1 "error_pattern" "Provide.*qm.*only in source or destination" \
43-
python3 "$QMCTL_SCRIPT" cp /some/path invalid:/path/format
41+
qmctl cp /some/path invalid:/path/format
4442

4543
# Test 9: Copy both paths with qm: prefix (invalid)
4644
run_test "Copy both paths with container prefix" 1 "error_pattern" "Provide.*qm.*only in source or destination" \
47-
python3 "$QMCTL_SCRIPT" cp qm:/path1 qm:/path2
45+
qmctl cp qm:/path1 qm:/path2
4846

4947
# Test 10: Show with invalid subcommand
5048
run_test "Show invalid subcommand" 2 "error_pattern" "invalid choice" \
51-
python3 "$QMCTL_SCRIPT" show invalid_show_command
49+
qmctl show invalid_show_command
5250

5351
# Test 11: Help command (should succeed)
5452
run_test "Help command" 0 "output_pattern" "usage" \
55-
python3 "$QMCTL_SCRIPT" --help
53+
qmctl --help
5654

5755
# Test 12: JSON flag with exec but no command
5856
run_test "JSON exec without command" 22 "none" "" \
59-
python3 "$QMCTL_SCRIPT" exec --json
57+
qmctl exec --json
6058

6159
# Test 13: Verbose mode with error
6260
run_test "Verbose mode with error" 22 "error_pattern" "No command provided" \
63-
python3 "$QMCTL_SCRIPT" -v exec
61+
qmctl -v exec
6462

6563
# Test 14: Empty string as command
6664
run_test "Empty string command" 1 "error_pattern" "Failed to execute" \
67-
python3 "$QMCTL_SCRIPT" exec ""
65+
qmctl exec ""
6866

6967
# Test 15: Exec with non-existent command
7068
run_test "Exec non-existent command" 1 "error_pattern" "not found" \
71-
python3 "$QMCTL_SCRIPT" exec /definitely/nonexistent/command/12345
69+
qmctl exec /definitely/nonexistent/command/12345
7270

7371
# Test 16: Execin with non-existent nested container
7472
run_test "Execin non-existent container" 1 "error_pattern" "failed" \
75-
python3 "$QMCTL_SCRIPT" execin nonexistent_nested_container echo hello
73+
qmctl execin nonexistent_nested_container echo hello
7674

7775
# Test 17: Copy non-existent source file
7876
nonexistent_file="/tmp/qmctl_test_nonexistent_$(date +%s%N)"
7977
run_test "Copy non-existent source" 1 "error_pattern" "could not be found" \
80-
python3 "$QMCTL_SCRIPT" cp "$nonexistent_file" qm:/tmp/
78+
qmctl cp "$nonexistent_file" qm:/tmp/
8179

8280
# Test 18: Very long command
8381
long_command=$(printf 'a%.0s' {1..1000})
8482
run_test "Very long command" 1 "error_pattern" "File name too long" \
85-
python3 "$QMCTL_SCRIPT" exec "$long_command"
83+
qmctl exec "$long_command"
8684

8785
# Test 19: Special characters in command
8886
# shellcheck disable=SC2016 # Single quotes intentional to prevent expansion
8987
run_test "Special characters command" 1 "error_pattern" "not found" \
90-
python3 "$QMCTL_SCRIPT" exec 'command_with_$pecial_ch@rs_!@#$%^&*()'
88+
qmctl exec 'command_with_$pecial_ch@rs_!@#$%^&*()'
9189

9290
# All tests passed
9391
pass_message "All qmctl error handling tests completed successfully"

tests/qmctl-test/scripts/qmctl_exec.sh

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
# shellcheck disable=SC1091
44
. ../e2e/lib/utils
55

6-
QMCTL_SCRIPT="../../tools/qmctl/qmctl"
76
CONTAINER_NAME="alpine-podman"
87

98
# Cleanup function that will be called on script exit
109
cleanup() {
11-
if python3 $QMCTL_SCRIPT exec test -f /tmp/test_file > /dev/null 2>&1; then
10+
if qmctl exec test -f /tmp/test_file > /dev/null 2>&1; then
1211
info_message "Removing test file..."
13-
python3 $QMCTL_SCRIPT exec rm -f /tmp/test_file > /dev/null 2>&1 || true
12+
qmctl exec rm -f /tmp/test_file > /dev/null 2>&1 || true
1413
fi
1514
}
1615

@@ -19,21 +18,21 @@ trap cleanup EXIT
1918

2019
# Cleanup any existing container from previous runs
2120
info_message "Cleaning up any existing test container..."
22-
python3 $QMCTL_SCRIPT exec podman rm -f "$CONTAINER_NAME" > /dev/null 2>&1 || true
21+
qmctl exec podman rm -f "$CONTAINER_NAME" > /dev/null 2>&1 || true
2322

2423
# Create new container
2524
info_message "Creating test container..."
26-
python3 $QMCTL_SCRIPT exec podman run -d --name "$CONTAINER_NAME" --replace alpine tail -f /dev/null
25+
qmctl exec podman run -d --name "$CONTAINER_NAME" --replace alpine tail -f /dev/null
2726

2827
# Verify container was created
29-
if ! python3 $QMCTL_SCRIPT exec podman ps -a | grep -q "$CONTAINER_NAME"; then
28+
if ! qmctl exec podman ps -a | grep -q "$CONTAINER_NAME"; then
3029
fail_message "Container $CONTAINER_NAME was not created"
3130
exit 1
3231
fi
3332

3433
# Test that touch command returns exit code 0 with empty output
3534
info_message "Testing touch command with empty output..."
36-
touch_output=$(python3 $QMCTL_SCRIPT exec touch /tmp/test_file 2>&1)
35+
touch_output=$(qmctl exec touch /tmp/test_file 2>&1)
3736
touch_exit=$?
3837

3938
if [ $touch_exit -ne 0 ]; then

tests/qmctl-test/scripts/qmctl_execin.sh

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,38 @@
33
# shellcheck disable=SC1091
44
. ../e2e/lib/utils
55

6-
QMCTL_SCRIPT="../../tools/qmctl/qmctl"
76
CONTAINER_NAME="alpine-podman"
87
TMP_FILE="/tmp/file-execin.txt"
98

109
# Ensure the alpine-podman container exists (create if needed)
11-
if ! python3 $QMCTL_SCRIPT exec podman ps -a | grep -q "$CONTAINER_NAME"; then
10+
if ! qmctl exec podman ps -a | grep -q "$CONTAINER_NAME"; then
1211
info_message "Creating $CONTAINER_NAME container for execin test..."
13-
python3 $QMCTL_SCRIPT exec podman run -d --name "$CONTAINER_NAME" alpine tail -f /dev/null
12+
qmctl exec podman run -d --name "$CONTAINER_NAME" alpine tail -f /dev/null
1413
fi
1514

1615
# Ensure the container is running
17-
python3 $QMCTL_SCRIPT exec podman start "$CONTAINER_NAME" > /dev/null 2>&1 || true
16+
qmctl exec podman start "$CONTAINER_NAME" > /dev/null 2>&1 || true
1817

1918
# Clean up any existing test file from previous runs
20-
python3 $QMCTL_SCRIPT execin "$CONTAINER_NAME" rm -f "$TMP_FILE" > /dev/null 2>&1 || true
19+
qmctl execin "$CONTAINER_NAME" rm -f "$TMP_FILE" > /dev/null 2>&1 || true
2120

2221
# Verify test file doesn't exist
23-
if python3 $QMCTL_SCRIPT execin "$CONTAINER_NAME" ls -la /tmp/ | grep -q "file-execin.txt"; then
22+
if qmctl execin "$CONTAINER_NAME" ls -la /tmp/ | grep -q "file-execin.txt"; then
2423
fail_message "The file $TMP_FILE still exists after cleanup"
2524
exit 1
2625
fi
2726

2827
# Create the test file
2928
info_message "Testing execin functionality..."
30-
python3 $QMCTL_SCRIPT execin "$CONTAINER_NAME" touch "$TMP_FILE"
29+
qmctl execin "$CONTAINER_NAME" touch "$TMP_FILE"
3130

3231
# Verify the file was created and can be read
33-
if ! python3 $QMCTL_SCRIPT execin "$CONTAINER_NAME" cat "$TMP_FILE" > /dev/null 2>&1; then
32+
if ! qmctl execin "$CONTAINER_NAME" cat "$TMP_FILE" > /dev/null 2>&1; then
3433
fail_message "The file $TMP_FILE was not created or cannot be read"
3534
exit 1
3635
fi
3736

3837
# Cleanup test file
39-
python3 $QMCTL_SCRIPT execin "$CONTAINER_NAME" rm -f "$TMP_FILE" > /dev/null 2>&1 || true
38+
qmctl execin "$CONTAINER_NAME" rm -f "$TMP_FILE" > /dev/null 2>&1 || true
4039

4140
pass_message "qmctl execin command executed successfully"

0 commit comments

Comments
 (0)