From e7717efa520d4f92f2ab4bf9d84b6dbfff3a2a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Wed, 22 Jul 2026 19:49:57 -0300 Subject: [PATCH] test(openbmc): characterize 503 retry scheduling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com> --- .../autotest/testcase/openbmc_unit/cases0 | 8 + xCAT-test/unit/openbmc_503_retry.t | 183 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 xCAT-test/autotest/testcase/openbmc_unit/cases0 create mode 100644 xCAT-test/unit/openbmc_503_retry.t diff --git a/xCAT-test/autotest/testcase/openbmc_unit/cases0 b/xCAT-test/autotest/testcase/openbmc_unit/cases0 new file mode 100644 index 000000000..c6f5214ea --- /dev/null +++ b/xCAT-test/autotest/testcase/openbmc_unit/cases0 @@ -0,0 +1,8 @@ +start:openbmc_503_retry_unit_tests +description:Run the OpenBMC 503 retry Perl unit tests through xcattest +os:Linux +label:mn_only,ci_test,openbmc,unit,openbmc_unit +cmd:prove -I/opt/xcat/lib/perl -I/opt/xcat/lib/perl/xCAT /opt/xcat/share/xcat/tools/autotest/unit/openbmc_*.t +check:rc==0 +check:output=~All tests successful +end diff --git a/xCAT-test/unit/openbmc_503_retry.t b/xCAT-test/unit/openbmc_503_retry.t new file mode 100644 index 000000000..7bb272502 --- /dev/null +++ b/xCAT-test/unit/openbmc_503_retry.t @@ -0,0 +1,183 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +no warnings 'once'; + +use File::Spec; +use FindBin; +use Test::More; + +my @plugin_candidates = ( + File::Spec->catfile( + $FindBin::Bin, '..', '..', 'xCAT-server', 'lib', 'xcat', 'plugins', + 'openbmc.pm' + ), +); +push @plugin_candidates, + File::Spec->catfile($ENV{XCATROOT}, 'lib', 'perl', 'xCAT_plugin', 'openbmc.pm') + if defined $ENV{XCATROOT}; +push @plugin_candidates, '/opt/xcat/lib/perl/xCAT_plugin/openbmc.pm'; + +my ($plugin_path) = grep { -f } @plugin_candidates; +BAIL_OUT('Could not locate the OpenBMC plugin') if !defined $plugin_path; + +open my $plugin_fh, '<', $plugin_path + or BAIL_OUT("Could not open $plugin_path: $!"); +my $plugin_source = do { local $/; <$plugin_fh> }; +close $plugin_fh; + +sub extract_subroutine { + my ($name) = @_; + my ($subroutine) = + $plugin_source =~ /(^sub \Q$name\E \{.*?^\}\n)(?=\n#-+)/ms; + BAIL_OUT("Could not extract $name from $plugin_path") + if !defined $subroutine; + return $subroutine; +} + +my $retry_after = extract_subroutine('retry_after'); +my $deal_with_response = extract_subroutine('deal_with_response'); + +{ + package xCAT::SvrUtils; + + our @messages; + + sub sendmsg { + push @messages, [@_]; + } +} + +{ + package OpenBMC503Response; + + sub new { + return bless {}, shift; + } + + sub status_line { + return '503 Service Unavailable'; + } +} + +## no critic (BuiltinFunctions::ProhibitStringyEval) +my $loaded = eval <<"HARNESS"; +package OpenBMC503Harness; +no strict 'vars'; +use warnings; +use JSON; +$retry_after +$deal_with_response +1; +HARNESS +## use critic +BAIL_OUT("Could not load the OpenBMC response harness: $@") if !$loaded; + +$::RESPONSE_OK = '200 OK'; +$::RESPONSE_SERVICE_UNAVAILABLE = '503 Service Unavailable'; +$::UPLOAD_ACTIVATE_STREAM = 0; +$::UPLOAD_AND_ACTIVATE = 0; + +my $node = 'node01'; +my $response = OpenBMC503Response->new(); +my $callback = sub { }; + +$OpenBMC503Harness::callback = $callback; +$OpenBMC503Harness::wait_node_num = 1; +$OpenBMC503Harness::xcatdebugmode = 0; +$OpenBMC503Harness::next_status{LOGIN_RESPONSE} = 'RPOWER_STATUS_REQUEST'; + +for my $attempt (1 .. 3) { + subtest "503 retry attempt $attempt is scheduled" => sub { + my $handle = "handle-$attempt"; + $OpenBMC503Harness::handle_id_node{$handle} = $node; + $OpenBMC503Harness::node_info{$node}{cur_status} = + 'RPOWER_STATUS_RESPONSE'; + delete $OpenBMC503Harness::node_wait{$node}; + + my @warnings; + my $before = time(); + { + local $SIG{__WARN__} = sub { push @warnings, @_ }; + OpenBMC503Harness::deal_with_response($handle, $response); + } + my $after = time(); + + is( + $OpenBMC503Harness::node_info{$node}{_503_retries}, + $attempt, + 'retry counter is incremented once' + ); + is( + $OpenBMC503Harness::node_info{$node}{cur_status}, + 'RPOWER_STATUS_REQUEST', + 'response state returns to its request state' + ); + cmp_ok( + $OpenBMC503Harness::node_wait{$node}, + '>=', + $before + 3, + 'retry deadline is at least three seconds from entry' + ); + cmp_ok( + $OpenBMC503Harness::node_wait{$node}, + '<=', + $after + 3, + 'retry deadline is no more than three seconds from return' + ); + ok( + !exists $OpenBMC503Harness::handle_id_node{$handle}, + 'completed response handle is removed' + ); + is( + $OpenBMC503Harness::wait_node_num, + 1, + 'node remains active while a retry is pending' + ); + is_deeply(\@xCAT::SvrUtils::messages, [], 'no error is reported'); + is_deeply(\@warnings, [], 'no warning is emitted'); + }; +} + +subtest 'fourth 503 exhausts retries' => sub { + my $handle = 'handle-4'; + $OpenBMC503Harness::handle_id_node{$handle} = $node; + $OpenBMC503Harness::node_info{$node}{cur_status} = + 'RPOWER_STATUS_RESPONSE'; + delete $OpenBMC503Harness::node_wait{$node}; + + my @warnings; + { + local $SIG{__WARN__} = sub { push @warnings, @_ }; + OpenBMC503Harness::deal_with_response($handle, $response); + } + + is( + $OpenBMC503Harness::node_info{$node}{_503_retries}, + 4, + 'retry counter records the exhausted attempt' + ); + is( + $OpenBMC503Harness::node_info{$node}{cur_status}, + 'RPOWER_STATUS_RESPONSE', + 'terminal response state is not rescheduled' + ); + ok( + !exists $OpenBMC503Harness::node_wait{$node}, + 'no retry deadline is set' + ); + ok( + !exists $OpenBMC503Harness::handle_id_node{$handle}, + 'completed response handle is removed' + ); + is($OpenBMC503Harness::wait_node_num, 0, 'node is completed'); + is_deeply( + \@xCAT::SvrUtils::messages, + [[[1, '503 Service Unavailable'], $callback, $node]], + 'the existing terminal error is reported' + ); + is_deeply(\@warnings, [], 'no warning is emitted'); +}; + +done_testing();