Skip to content

Add Salt package download benchmark - #12341

Open
AkashKumar7902 wants to merge 17 commits into
uyuni-project:masterfrom
AkashKumar7902:testsuite/package-download-benchmark
Open

AkashKumar7902 wants to merge 17 commits into
uyuni-project:masterfrom
AkashKumar7902:testsuite/package-download-benchmark

Conversation

@AkashKumar7902

@AkashKumar7902 AkashKumar7902 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What does this PR change?

Adds an opt-in Cucumber benchmark that downloads every binary RPM from one Uyuni software channel on a configured set of Salt minions.

Benchmark flow

  1. Read the channel label and Salt minion IDs from environment variables.

  2. Capture the channel's binary-RPM snapshot through the Uyuni API and confirm that every minion is registered and subscribed.

  3. Find the ready Uyuni server pod.

  4. Prepare the minions with:

    • state.apply channels
    • grains.item os_family osarch
    • pkg.get_repo
    • pkg.refresh_db
    • pkg.list_repo_pkgs
  5. Clear the RPM payload cache outside the measured interval.

  6. Run one concurrent Salt cmd.run_all workload on all selected minions:

    zypper --quiet --non-interactive --no-refresh download \
      --all-matches \
      --repo susemanager:<channel-label> \
      '*'
  7. Inventory the downloaded RPMs, match them to the channel checksums, and confirm that the channel snapshot remained unchanged.

  8. Write a structured JSON result.

Commands follow this path:

testsuite controller
  -> get_target('localhost').run_local
  -> kubectl exec into the Uyuni server pod
  -> salt --list <minions>
  -> Salt minions

Only the package download workload is timed. Setup, cache cleanup, and verification run outside the measured interval. The RPMs are downloaded into each minion's zypper package cache and are not installed.

The benchmark expects an existing channel and existing subscriptions. It does not create channels, subscribe systems, install a Helm release, or configure a StorageClass.

Configuration

Prerequisites

  • The testsuite controller can reach the Kubernetes cluster with kubectl.
  • The Uyuni server pod is running and ready in the uyuni namespace.
  • The selected Salt minions are registered, reachable, and subscribed to the configured channel.
  • The selected minions are SUSE systems with the same architecture.
  • The channel contains binary RPM packages and is available on every selected minion.
  • Initialize the Cobbler API client lazily in cobbler_steps.rb #12240 is present so a run set without Cobbler scenarios does not initialize the Cobbler client during Cucumber startup.

Required inputs

Variable Description Example
UYUNI_BENCH_MINIONS JSON array containing the exact Salt minion IDs ["minion-1.example.test","minion-2.example.test"]
UYUNI_BENCH_CHANNEL Uyuni software channel label uyuni-bench-channel

The normal testsuite environment must also provide SERVER and its API configuration.

Optional inputs

Variable Default Description
UYUNI_BENCH_TIMEOUT_SECONDS 14400 Timeout for the measured all-minion download workload
UYUNI_BENCH_STORAGE_CLASS unset StorageClass name recorded as result metadata
SCENARIO_HARD_LIMIT workload timeout + 1800 seconds Overall Cucumber scenario watchdog override

Control operations use a maximum timeout of 600 seconds. UYUNI_BENCH_STORAGE_CLASS does not select or configure storage.

How to run

Run the dedicated Rake task from a configured testsuite controller:

source /root/.bashrc
cd /root/spacewalk/testsuite

export UYUNI_BENCH_MINIONS='["minion-1.example.test","minion-2.example.test"]'
export UYUNI_BENCH_CHANNEL='uyuni-bench-channel'
export UYUNI_BENCH_TIMEOUT_SECONDS=14400
export UYUNI_BENCH_STORAGE_CLASS='nfs-csi'

bundle exec rake cucumber:kubernetes_package_download_benchmark

If StorageClass metadata is not needed, omit UYUNI_BENCH_STORAGE_CLASS.

Results are written to:

testsuite/results/package-download/<timestamp>-<pid>/result.json

The result contains the measured duration, per-minion command status, package and payload counts, downloaded bytes, channel snapshot digest, and verification errors.

Codespace

Create CodeSpace

GUI diff

No difference.

  • DONE

Documentation

  • The configuration and run instructions are included in this PR description.

  • DONE

Test coverage

Validated with:

  • Ruby syntax checks for the benchmark steps and Rakefile;

  • RuboCop for the benchmark step definitions;

  • focused checks for API snapshot mapping and the Salt preflight sequence;

  • focused checks for controller target selection, kubectl exec serialization, Salt arguments, zypper arguments, and timeout values;

  • focused checks for RPM cache verification and result schema;

  • run-set linkage validation;

  • git diff --check.

  • DONE

Links

Issue(s): openSUSE/mentoring#252
Related: #12077
Depends on: #12240
Related Sumaform storage work: uyuni-project/sumaform#2246
Port(s): none

  • DONE

Changelogs

  • No changelog needed

Re-run a test

  • Re-run test "changelog_test"
  • Re-run test "backend_unittests_pgsql"
  • Re-run test "java_pgsql_tests"
  • Re-run test "schema_migration_test_pgsql"
  • Re-run test "susemanager_unittests"
  • Re-run test "frontend_checks"
  • Re-run test "spacecmd_unittests"

Before you merge

Check How to branch and merge properly!

Comment thread testsuite/features/step_definitions/package_download_benchmark_steps.rb Outdated
Comment thread testsuite/features/step_definitions/package_download_benchmark_steps.rb Outdated
@AkashKumar7902
AkashKumar7902 force-pushed the testsuite/package-download-benchmark branch from 68355d2 to c8594d3 Compare August 17, 2026 20:13
@AkashKumar7902

AkashKumar7902 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

@Pablogoliva Review updates are pushed in c8594d3a52 on top of current master:

  • removed @long_running while keeping an active run-set-specific watchdog;
  • replaced every custom Open3 execution path with RemoteNode#run on the registered localhost controller target;
  • preserved shell-safe argv handling, separate stdout/stderr, explicit exit status, and TERM/KILL timeouts;
  • verified that plain salt is the established server-side CLI while venv-salt-* selection is minion-side; and
  • corrected the PR description so it no longer claims closed Sumaform Refreshing repositories on .deb servers through Uyuni proxy very slow #2241 supplies UYUNI_BENCH_MINIONS.

Ruby syntax, targeted RuboCop, runner/quoting assertions, and diff checks pass. A new full benchmark run still requires deployed Uyuni infrastructure and minions.

Comment thread testsuite/features/step_definitions/package_download_benchmark_steps.rb Outdated

@Pablogoliva Pablogoliva left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the way to use system commands.

@AkashKumar7902
AkashKumar7902 force-pushed the testsuite/package-download-benchmark branch from c8594d3 to 2d693f5 Compare August 18, 2026 13:07
@AkashKumar7902

AkashKumar7902 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@Pablogoliva Follow-up on the system-command review is complete on current head.

  • The benchmark now calls the standard RemoteNode#run_local method directly at every controller execution site.
  • The shared SSH helper now actually forwards the caller's timeout to the SSH channel; a real transport timeout reports 124, while unrelated transport failures remain -1.

I also ran the full mounted-volume pipeline against the external RKE2 environment. The isolated checkout included the declared #12240 dependency; its testsuite directory was bind-mounted into the controller, with kubectl and the RKE2 kubeconfig mounted read-only.

1 scenario passed; 8 / 8 steps passed
measured workload:        264.839248 seconds
successful Salt targets:  2 / 2
verified packages:        6,054 / 6,054
verified payload bytes:   37,105,623,752
missing/mismatched/extra: 0 / 0 / 0
timed_out:                false
uncertain_completion:     false
channel snapshot stable:  yes

The PR description now contains the updated timeout model and validation result. Please re-review the current head when convenient.

code = nil
exception = nil
begin
stdout, stderr, code = get_target('localhost').run_local(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stdout, stderr, code = get_target('localhost').run_local(
stdout, stderr, code = get_target('controller').run_local(

Also in the other occurrences.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the existing code already runs on the controller.

plus, the controller is not defined in:
https://github.com/uyuni-project/uyuni/blob/master/testsuite/features/support/constants.rb

package_download_benchmark_salt_call(
inputs,
pod,
'cmd.run_all',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the matter of this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It runs a Python inventory command on each minion to collect the paths and sizes of files under /var/cache/zypp/packages. The returned inventory is used for post-download verification.

Order:

  1. Before benchmark
    cmd.run_all → clear old RPM cache

  2. During benchmark (timed)
    cmd.run_all → run zypper download

  3. After benchmark
    cmd.run_all → inventory downloaded RPM files

  4. Verification
    Compare inventory with expected channel packages

So line 848 is post-benchmark verification and is not included in the measured duration.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Hello! Thanks for contributing to our project.
Acceptance tests will take some time (approx. 1h), please be patient ☕

You can see the progress at the end of this page and at https://github.com/uyuni-project/uyuni/pull/12341/checks
Once tests finish, if they fail, you can check 👀 the cucumber report. See the link at the output of the action.
You can also check the artifacts section, which contains the logs at https://github.com/uyuni-project/uyuni/pull/12341/checks.

If you are unsure the failing tests are related to your code, you can check the "reference jobs". These are jobs that run on a scheduled time with code from master. If they fail for the same reason as your build, it means the tests or the infrastructure are broken. If they do not fail, but yours do, it means it is related to your code.

Reference tests:

KNOWN ISSUES

Sometimes the build can fail when pulling new jar files from download.opensuse.org . This is a known limitation. Given this happens rarely, when it does, all you need to do is rerun the test. Sorry for the inconvenience.

For more tips on troubleshooting, see the troubleshooting guide.

Happy hacking!

status: workload_errors.empty? ? 'passed' : 'failed',
command: zypper,
timeout_seconds: timeout,
timed_out: workload_error.to_s.include?('exited with 124'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exit code is not in zypper's man page with the others… Where does it come from?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it comes from network_utils.rb where we throw this exit code when timeout happens

@@ -0,0 +1,18 @@
# Copyright (c) 2026 SUSE LLC

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright should be yours: change the SUSE LLC with your full name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How comes that you add a new run set? Shouldn't this run after the reposync benchmark to reuse the imported repository? In which case it should be in the same run_set.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I though of keeping it separate , will be making changes to accomodate both

rescue Timeout::Error
puts "SSH operation timed out after #{timeout} seconds."
stderr = "SSH operation timed out after #{timeout} seconds."
exit_code = 124

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this exit_code coming from?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is a timeout error code which we are synthesizing

inputs = @package_download_inputs
api = $api_test

# 1. Read the current channel state from the Uyuni API.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI tends to add numbered comments, but this is a pain to maintain in the long run. Remove the numbered list items from them please.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, should i just remove the numbers, or the enitre comment ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated, removed the numbers

)
end

Given('a ready server pod is reachable from the benchmark controller') do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function needs to be shared with the other PR as it's a duplicate. Better base this PR on the other one to share code with it

inputs = @package_download_inputs
pod = @package_download_pod

# 1. Run `state.apply channels` so Uyuni updates the assigned software channel

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again numbered comments…

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

'--all-matches',
'--repo',
inputs[:repo_alias],
'*'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could have issue with packages conflicting each other in the repo

@AkashKumar7902
AkashKumar7902 force-pushed the testsuite/package-download-benchmark branch 2 times, most recently from 09c97e8 to 2229bd8 Compare August 28, 2026 09:31
Timeout.timeout(timeout) do # Enforce timeout on the entire SSH operation
Net::SSH.start(host, nil, port: port, verify_host_key: :never, timeout: timeout, keepalive: true, max_pkt_size: buffer_size, config: true) do |ssh|
stdout, stderr, exit_code = ssh_exec!(ssh, command, timeout: DEFAULT_TIMEOUT)
stdout, stderr, exit_code = ssh_exec!(ssh, command, timeout: timeout)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave the previous timeout pls

AkashKumar7902 and others added 17 commits September 20, 2026 20:07
Snapshot all binary RPMs in the configured Uyuni channel and verify that every target minion is subscribed.

Run one concurrent zypper download --all-matches command per minion, record the measured duration, and verify every cached artifact against the frozen channel checksum set.

Signed-off-by: Akash Kumar <meakash7902@gmail.com>

Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Run kubectl and Salt through the testsuite controller target while preserving process timeouts and separated command output.

Keep the normal scenario watchdog enabled with a benchmark-specific limit derived from the configured download timeout.

Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Stop converting Ruby timeouts into a shell-style exit code and remove result metadata that depends on parsing it.

Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
Move the feature into the kubernetes_storage_benchmark run set so it
reuses the channel imported by the reposync benchmark: wait for the
channel repodata, subscribe the configured minions through the API, and
share the reposync helpers for the server pod, integer inputs and the
storage backend label. Drop the network_utils.rb timeout change and
require DEFAULT_TIMEOUT to cover the download timeout instead. Replace
the inline Python cache inventory with a find command.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Akash Kumar <91385321+AkashKumar7902@users.noreply.github.com>
@AkashKumar7902
AkashKumar7902 force-pushed the testsuite/package-download-benchmark branch from 70fb1fb to 2c1ac3c Compare September 20, 2026 14:53

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants