Skip to content

Commit 27494cb

Browse files
committed
Add tool to compare built ipa and master ipa initramfs
Signed-off-by: peppi-lotta <[email protected]>
1 parent 1e0f3cf commit 27494cb

File tree

1 file changed

+329
-0
lines changed

1 file changed

+329
-0
lines changed

hack/ipa_debug_tools.sh

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
# The path to the directory that holds this script
5+
CURRENT_SCRIPT_DIR="$(dirname -- "$(readlink -f "${BASH_SOURCE[0]}")")"
6+
BASE_DEBUG_DIR="/tmp/debug-initramfs"
7+
mkdir -p ${BASE_DEBUG_DIR}
8+
BASE_HELPER_FILE_DIR=$(sudo mktemp -dp "${BASE_DEBUG_DIR}")
9+
10+
# Fix permissions so current user can create temp files
11+
sudo chown "${USER}:$(id -gn)" "${BASE_HELPER_FILE_DIR}"
12+
13+
# Set up cleanup trap to run when script exits
14+
trap clean_up_debug_helpers EXIT
15+
16+
# set_up_debug_dirs [file] [url]
17+
#
18+
# This ceates a minimal set up in /tmp/debug-initramfs directory for
19+
# comparing built ipa initramfs the master ipa initramfs.
20+
#
21+
# Usage examples:
22+
# ./ipa_debug_tools.sh set_up_debug_dirs
23+
# ./ipa_debug_tools.sh set_up_debug_dirs ipa-centos10-master.tar.gz
24+
# ./ipa_debug_tools.sh set_up_debug_dirs ipa-centos10-master.tar.gz https://tarballs.opendev.org/openstack/ironic-python-agent/dib/
25+
#
26+
# - [file]: Name of tar file o be downloaded [default: ipa-centos10-master.tar.gz]
27+
# - [url]: Url to ironic python agent tarball archive [default: https://tarballs.opendev.org/openstack/ironic-python-agent/dib/]
28+
#
29+
set_up_debug_dirs() {
30+
file_name="${1:-ipa-centos9-master.tar.gz}"
31+
url="${2:-https://tarballs.opendev.org/openstack/ironic-python-agent/dib/}"
32+
full_path="${url}${file_name}"
33+
file_base_name=$(echo "${file_name}"| cut -d'.' -f 1)
34+
35+
clean_up_debug_dirs
36+
37+
# Export DISABLE_UPLOAD, ENABLE_BOOTSTRAP_TEST, TEST_IN_CI to allow local
38+
# test run.
39+
export DISABLE_UPLOAD="true"
40+
export ENABLE_BOOTSTRAP_TEST="false"
41+
export TEST_IN_CI="false"
42+
43+
# Build ipa with build_ipa.sh. Search for the script in a path that is
44+
# relative to this files location.
45+
cd "${CURRENT_SCRIPT_DIR}/../jenkins/scripts/dynamic_worker_workflow"
46+
if [[ ! -x ./build_ipa.sh ]]; then
47+
echo "Error: build_ipa.sh not found"
48+
exit 1
49+
fi
50+
51+
if ! ./build_ipa.sh; then
52+
echo "build_ipa.sh failed, will check for artifact..."
53+
fi
54+
55+
# Even if build_ipa.sh exits with error this script can continue as long as
56+
# /tmp/dib/ironic-python-agent.initramfs has been created
57+
if [[ ! -f /tmp/dib/ironic-python-agent.initramfs ]]; then
58+
echo "Required file '/tmp/dib/ironic-python-agent.initramfs' not created, exiting."
59+
exit 1
60+
fi
61+
62+
# Unzip ironic-python-agent.initramfs
63+
sudo mkdir -p "${BASE_DEBUG_DIR}/build-ipa-initramfs"
64+
sudo cp /tmp/dib/ironic-python-agent.initramfs "${BASE_DEBUG_DIR}/"
65+
cd "${BASE_DEBUG_DIR}/build-ipa-initramfs"
66+
gunzip -c ../ironic-python-agent.initramfs | sudo cpio -id
67+
68+
# Get master ironic-python-agent.initramfs and unzip
69+
sudo mkdir -p "${BASE_DEBUG_DIR}/master-ipa-initramfs"
70+
cd "${BASE_DEBUG_DIR}"
71+
sudo wget "${full_path}"
72+
sudo tar -xzf "${file_name}"
73+
cd master-ipa-initramfs
74+
gunzip -c "../${file_base_name}.initramfs" | sudo cpio -id
75+
}
76+
77+
# -----------------------------------------------------------------------------
78+
# compare_dir_sizes <relative_path> [base_build_dir] [base_master_dir]
79+
#
80+
# Compares the sizes of files and directories at the given <relative_path>
81+
# inside two extracted initramfs directories (or any two directories).
82+
#
83+
# Usage examples:
84+
# ./ipa_debug_tools.sh compare_dir_sizes
85+
# ./ipa_debug_tools.sh compare_dir_sizes usr
86+
# ./ipa_debug_tools.sh compare_dir_sizes usr/lib ~/ipa-build-initramfs ~/ipa-initramfs
87+
# ./ipa_debug_tools.sh compare_dir_sizes . ~/ipa-build-initramfs ~/ipa-initramfs
88+
#
89+
# - <relative_path>: Path inside the compared directories (e.g., usr, usr/lib)
90+
# - [base_build_dir]: First base directory (default: /tmp/debug-initramfs/build-ipa-initramfs)
91+
# - [base_master_dir]: Second base directory (default: /tmp/debug-initramfs/master-ipa-initramfs)
92+
#
93+
# The function prints a table of entries, their sizes in both directories,
94+
# and the size difference. It also lists files only present in one directory.
95+
# -----------------------------------------------------------------------------
96+
compare_dir_sizes() {
97+
path="${1:-}"
98+
build_dir_base="${2:-${BASE_DEBUG_DIR}/build-ipa-initramfs}"
99+
master_dir_base="${3:-${BASE_DEBUG_DIR}/master-ipa-initramfs}"
100+
101+
build_dir="${build_dir_base}/${path}"
102+
master_dir="${master_dir_base}/${path}"
103+
104+
# Color codes
105+
RED='\033[0;31m'
106+
GREEN='\033[0;32m'
107+
YELLOW='\033[1;33m'
108+
BLUE='\033[0;34m'
109+
PURPLE='\033[0;35m'
110+
CYAN='\033[0;36m'
111+
NC='\033[0m' # No Color
112+
BOLD='\033[1m'
113+
114+
echo -e "${CYAN}Comparing directories:${NC}"
115+
echo -e " ${BLUE}Build IPA:${NC} ${build_dir}"
116+
echo -e " ${GREEN}Master IPA:${NC} ${master_dir}"
117+
echo ""
118+
119+
# Print table header
120+
printf "${BOLD}%-40s %-22s %-22s %-22s${NC}\n" "Directory/File" "Build" "Master" "Diff"
121+
echo "======================================================================================================="
122+
123+
tmpfile=$(mktemp -p "${BASE_HELPER_FILE_DIR}")
124+
build_dir_list=$(mktemp -p "${BASE_HELPER_FILE_DIR}")
125+
master_dir_list=$(mktemp -p "${BASE_HELPER_FILE_DIR}")
126+
checked=0
127+
skipped=0
128+
129+
# Compare entries present in both directories
130+
find "${build_dir}" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort > "${build_dir_list}"
131+
find "${master_dir}" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort > "${master_dir_list}"
132+
comm -12 "${build_dir_list}" "${master_dir_list}" | while IFS= read -r name; do
133+
# Get size in bytes for each entry
134+
bsize=$(du -sb "${build_dir}/${name}" 2>/dev/null | cut -f1)
135+
isize=$(du -sb "${master_dir}/${name}" 2>/dev/null | cut -f1)
136+
# Calculate absolute difference
137+
diff=$(( bsize > isize ? bsize - isize : isize - bsize ))
138+
echo -e "${name}\t${bsize}\t${isize}\t${diff}"
139+
done | sort -k4,4nr > "${tmpfile}"
140+
141+
# Print entries with size differences
142+
while IFS=$'\t' read -r name bsize isize diff; do
143+
checked=$((checked + 1))
144+
if [[ "${diff}" -eq 0 ]]; then
145+
skipped=$((skipped + 1))
146+
continue
147+
fi
148+
149+
# Color code based on size difference magnitude
150+
if [[ "${diff}" -gt 10485760 ]]; then # > 10MB
151+
diff_color="${RED}"
152+
elif [[ "${diff}" -gt 1048576 ]]; then # > 1MB
153+
diff_color="${YELLOW}"
154+
else
155+
diff_color="${GREEN}"
156+
fi
157+
158+
printf "%-40s ${BLUE}%-22s${NC} ${GREEN}%-22s${NC} ${diff_color}%-21s${NC}\n" \
159+
"${name}" \
160+
"$(numfmt --to=iec "${bsize}")" \
161+
"$(numfmt --to=iec "${isize}")" \
162+
"$(numfmt --to=iec "${diff}")"
163+
done < "${tmpfile}"
164+
165+
echo -e "\n${PURPLE}Summary:${NC} Checked ${BOLD}${checked}${NC} common entries, skipped ${BOLD}${skipped}${NC} identical sizes."
166+
echo "======================================================================================================="
167+
168+
# List files only in build_dir
169+
echo -e "\n${BLUE}Only in Build IPA (${build_dir}):${NC}"
170+
echo "-------------------------------------------------------------------------------------------------------"
171+
comm -23 "${build_dir_list}" "${master_dir_list}" | while IFS= read -r name; do
172+
bsize=$(du -sb "${build_dir}/${name}" 2>/dev/null | cut -f1)
173+
echo -e "${bsize}\t${name}"
174+
done | sort -k1,1nr | while IFS=$'\t' read -r bsize name; do
175+
printf "${BLUE}%-40s %-22s${NC}\n" "${name}" "$(numfmt --to=iec "${bsize}")"
176+
done
177+
178+
echo "======================================================================================================="
179+
180+
# List files only in master_dir
181+
echo -e "\n${GREEN}Only in Master IPA (${master_dir}):${NC}"
182+
echo "-------------------------------------------------------------------------------------------------------"
183+
comm -13 "${build_dir_list}" "${master_dir_list}" | while IFS= read -r name; do
184+
isize=$(du -sb "${master_dir}/${name}" 2>/dev/null | cut -f1)
185+
echo -e "${isize}\t${name}"
186+
done | sort -k1,1nr | while IFS=$'\t' read -r isize name; do
187+
printf "${GREEN}%-40s %-22s${NC}\n" "${name}" "$(numfmt --to=iec "${isize}")"
188+
done
189+
190+
# Print some empty lines for clarity
191+
echo -e "\n\n\n"
192+
}
193+
194+
# Print a rpm package list. The print omits packages that are identical in both
195+
#the built ipa and master.
196+
# Check if packages are a part of CentOS Stream in https://pkgs.org
197+
compare_rpm_packages() {
198+
build_dir="${1:-${BASE_DEBUG_DIR}/build-ipa-initramfs}"
199+
master_dir="${2:-${BASE_DEBUG_DIR}/master-ipa-initramfs}"
200+
build_rpm_list=$(mktemp -p "${BASE_HELPER_FILE_DIR}")
201+
master_rpm_list=$(mktemp -p "${BASE_HELPER_FILE_DIR}")
202+
build_rpm_list_name_bases=$(mktemp -p "${BASE_HELPER_FILE_DIR}")
203+
master_rpm_list_name_bases=$(mktemp -p "${BASE_HELPER_FILE_DIR}")
204+
205+
sudo chroot "${build_dir}" rpm -qa | sort > "${build_rpm_list}"
206+
sudo chroot "${master_dir}" rpm -qa | sort > "${master_rpm_list}"
207+
208+
common_count=$(comm -12 "${build_rpm_list}" "${master_rpm_list}" | wc -l)
209+
echo -e "\nNumber of common packages: ${common_count}\n"
210+
# Extract base package names and join for fuzzy matching
211+
awk -F'-[0-9]' '{print $1}' "${build_rpm_list}" | sort > "${build_rpm_list_name_bases}"
212+
awk -F'-[0-9]' '{print $1}' "${master_rpm_list}" | sort > "${master_rpm_list_name_bases}"
213+
214+
printf "%-65s | %-65s\n" "In ${build_dir}" "In ${master_dir}"
215+
printf -- "----------------------------------------------------------------------------------------------------------------------------------\n"
216+
217+
join -a1 -a2 -e '' -o 1.1,2.1 "${build_rpm_list_name_bases}" "${master_rpm_list_name_bases}" | \
218+
while IFS=' ' read -r left right; do
219+
# Find the full package lines for each base name
220+
lfull=$(grep -m1 "^${left}-" "${build_rpm_list}" || echo "")
221+
rfull=$(grep -m1 "^${right}-" "${master_rpm_list}" || echo "")
222+
# Skip printing if both lines are non-empty and exactly the same
223+
if [[ -n "${lfull}" ]] && [[ "${lfull}" == "${rfull}" ]]; then
224+
continue
225+
fi
226+
printf "%-65s | %-65s\n" "${lfull}" "${rfull}"
227+
done
228+
}
229+
230+
# After debugging and comparison operations, temporary directories can be cleaned up.
231+
# This function removes the build and master filesystems. If this operation is run
232+
# the set_up_debug_dirs needs to be run again to create the compareble filesystems.
233+
# Usage:
234+
# ./ipa_debug_tools.sh clean_up_debug_dirs
235+
# -----------------------------------------------------------------------------
236+
clean_up_debug_dirs() {
237+
# Clean up debug directories
238+
sudo rm -rf "${BASE_DEBUG_DIR}" /tmp/dib ipa-file-injector.service 2> /dev/null
239+
}
240+
241+
# Clean up all helper temp files.
242+
clean_up_debug_helpers() {
243+
sudo rm -rf "${BASE_HELPER_FILE_DIR}" 2> /dev/null
244+
}
245+
246+
# Allow calling functions from command line
247+
# -----------------------------------------------------------------------------
248+
# Display usage information
249+
usage() {
250+
cat << EOF
251+
Usage: ${0} <command> [options]
252+
253+
COMMANDS:
254+
set_up_debug_dirs [file] [url]
255+
Set up debug directories for comparing IPA initramfs
256+
257+
Options:
258+
file Name of tar file to download (default: ipa-centos9-master.tar.gz)
259+
url URL to tarball archive (default: https://tarballs.opendev.org/openstack/ironic-python-agent/dib/)
260+
261+
compare_dir_sizes [relative_path] [base_build_dir] [base_master_dir]
262+
Compare sizes of files and directories
263+
264+
Options:
265+
relative_path Path inside compared directories (default: "")
266+
base_build_dir First base directory (default: /tmp/debug-initramfs/build-ipa-initramfs)
267+
base_master_dir Second base directory (default: /tmp/debug-initramfs/master-ipa-initramfs)
268+
269+
compare_rpm_packages [build_dir] [master_dir]
270+
Compare RPM packages between two directories
271+
272+
Options:
273+
build_dir Build directory (default: /tmp/debug-initramfs/build-ipa-initramfs)
274+
master_dir Master directory (default: /tmp/debug-initramfs/master-ipa-initramfs)
275+
276+
clean_up_debug_dirs
277+
Clean up all debug directories and temporary files
278+
279+
help, -h, --help
280+
Show this help message
281+
282+
EXAMPLE
283+
./ipa_debug_tools.sh set_up_debug_dirs
284+
./ipa_debug_tools.sh compare_dir_sizes
285+
./ipa_debug_tools.sh compare_rpm_packages
286+
./ipa_debug_tools.sh clean_up_debug_dirs
287+
288+
EOF
289+
}
290+
291+
# Argument parsing
292+
main() {
293+
# Check if no arguments provided
294+
if [[ ${#} -eq 0 ]]; then
295+
echo "Error: Command missing"
296+
echo ""
297+
echo "Available commands: set_up_debug_dirs, compare_dir_sizes, compare_rpm_packages, clean_up_debug_dirs"
298+
echo "Use '${0} help' for detailed usage information"
299+
exit 1
300+
fi
301+
302+
local command="${1}"
303+
shift
304+
305+
case "${command}" in
306+
help|-h|--help)
307+
usage
308+
exit 0
309+
;;
310+
set_up_debug_dirs|compare_dir_sizes|compare_rpm_packages|clean_up_debug_dirs)
311+
# Check if function exists and call it
312+
if declare -f "${command}" > /dev/null; then
313+
"${command}" "${@}"
314+
else
315+
echo "Error: Function '${command}' not implemented"
316+
exit 1
317+
fi
318+
;;
319+
*)
320+
echo "Error: Unknown command '${command}'"
321+
echo ""
322+
echo "Available commands: set_up_debug_dirs, compare_dir_sizes, compare_rpm_packages, clean_up_debug_dirs"
323+
echo "Use '${0} help' for detailed usage information"
324+
exit 1
325+
;;
326+
esac
327+
}
328+
329+
main "${@}"

0 commit comments

Comments
 (0)