From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 7502382CAA for ; Wed, 1 Dec 2021 09:55:17 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 6B469943C for ; Wed, 1 Dec 2021 09:55:17 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 515F99432 for ; Wed, 1 Dec 2021 09:55:16 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 2A78A44169 for ; Wed, 1 Dec 2021 09:55:16 +0100 (CET) From: Dominik Csapak To: pve-devel@lists.proxmox.com Date: Wed, 1 Dec 2021 09:55:14 +0100 Message-Id: <20211201085514.1648428-3-d.csapak@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20211201085514.1648428-1-d.csapak@proxmox.com> References: <20211201085514.1648428-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.181 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment LOTS_OF_MONEY 0.001 Huge... sums of money SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [calendarevent.pm] Subject: [pve-devel] [PATCH common 1/1] CalendarEvent: use rust implementation X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 01 Dec 2021 08:55:17 -0000 by replacing the parsing code and 'compute_next_event' by their PVE::RS::CalendarEvent equivalent adapt the tests, since we do not have access to the internal structure (and even if we had, it would be different) and the error messages are different the 'compute_next_event' and parsing tests still pass though Signed-off-by: Dominik Csapak --- src/PVE/CalendarEvent.pm | 251 +----------------------------------- test/calendar_event_test.pl | 42 +++--- 2 files changed, 23 insertions(+), 270 deletions(-) diff --git a/src/PVE/CalendarEvent.pm b/src/PVE/CalendarEvent.pm index 56e9923..e2bf53a 100644 --- a/src/PVE/CalendarEvent.pm +++ b/src/PVE/CalendarEvent.pm @@ -6,6 +6,7 @@ use Data::Dumper; use Time::Local; use PVE::JSONSchema; use PVE::Tools qw(trim); +use PVE::RS::CalendarEvent; # Note: This class implements a parser/utils for systemd like calendar exents # Date specification is currently not implemented @@ -43,259 +44,13 @@ sub parse_calendar_event { die "unable to parse calendar event - event is empty\n"; } - my $parse_single_timespec = sub { - my ($p, $max, $matchall_ref, $res_hash) = @_; - - if ($p =~ m/^((?:\*|[0-9]+))(?:\/([1-9][0-9]*))?$/) { - my ($start, $repetition) = ($1, $2); - if (defined($repetition)) { - $repetition = int($repetition); - $start = $start eq '*' ? 0 : int($start); - die "value '$start' out of range\n" if $start >= $max; - die "repetition '$repetition' out of range\n" if $repetition >= $max; - while ($start < $max) { - $res_hash->{$start} = 1; - $start += $repetition; - } - } else { - if ($start eq '*') { - $$matchall_ref = 1; - } else { - $start = int($start); - die "value '$start' out of range\n" if $start >= $max; - $res_hash->{$start} = 1; - } - } - } elsif ($p =~ m/^([0-9]+)\.\.([1-9][0-9]*)$/) { - my ($start, $end) = (int($1), int($2)); - die "range start '$start' out of range\n" if $start >= $max; - die "range end '$end' out of range\n" if $end >= $max || $end < $start; - for (my $i = $start; $i <= $end; $i++) { - $res_hash->{$i} = 1; - } - } else { - die "unable to parse calendar event '$p'\n"; - } - }; - - my $h = undef; - my $m = undef; - - my $matchall_minutes = 0; - my $matchall_hours = 0; - my $minutes_hash = {}; - my $hours_hash = {}; - - my $dowsel = join('|', keys %$dow_names); - - my $dow_hash; - - my $parse_dowspec = sub { - my ($p) = @_; - - if ($p =~ m/^($dowsel)$/i) { - $dow_hash->{$dow_names->{lc($1)}} = 1; - } elsif ($p =~ m/^($dowsel)\.\.($dowsel)$/i) { - my $start = $dow_names->{lc($1)}; - my $end = $dow_names->{lc($2)} || 7; - die "wrong order in range '$p'\n" if $end < $start; - for (my $i = $start; $i <= $end; $i++) { - $dow_hash->{($i % 7)} = 1; - } - } else { - die "unable to parse weekday specification '$p'\n"; - } - }; - - my @parts = split(/\s+/, $event); - my $utc = (@parts && uc($parts[-1]) eq 'UTC'); - pop @parts if $utc; - - - if ($parts[0] =~ m/$dowsel/i) { - my $dow_spec = shift @parts; - foreach my $p (split(',', $dow_spec)) { - $parse_dowspec->($p); - } - } else { - $dow_hash = { 0 => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5=> 1, 6 => 1 }; - } - - if (scalar(@parts) && $parts[0] =~ m/\-/) { - my $date_spec = shift @parts; - die "date specification not implemented"; - } - - my $time_spec = shift(@parts) // "00:00"; - my $chars = '[0-9*/.,]'; - - if ($time_spec =~ m/^($chars+):($chars+)$/) { - my ($p1, $p2) = ($1, $2); - foreach my $p (split(',', $p1)) { - $parse_single_timespec->($p, 24, \$matchall_hours, $hours_hash); - } - foreach my $p (split(',', $p2)) { - $parse_single_timespec->($p, 60, \$matchall_minutes, $minutes_hash); - } - } elsif ($time_spec =~ m/^($chars)+$/) { # minutes only - $matchall_hours = 1; - foreach my $p (split(',', $time_spec)) { - $parse_single_timespec->($p, 60, \$matchall_minutes, $minutes_hash); - } - - } else { - die "unable to parse calendar event\n"; - } - - die "unable to parse calendar event - unused parts\n" if scalar(@parts); - - if ($matchall_hours) { - $h = '*'; - } else { - $h = [ sort { $a <=> $b } keys %$hours_hash ]; - } - - if ($matchall_minutes) { - $m = '*'; - } else { - $m = [ sort { $a <=> $b } keys %$minutes_hash ]; - } - - return { h => $h, m => $m, dow => [ sort keys %$dow_hash ], utc => $utc }; -} - -sub is_leap_year($) { - return 0 if $_[0] % 4; - return 1 if $_[0] % 100; - return 0 if $_[0] % 400; - return 1; -} - -# mon = 0.. (Jan = 0) -sub days_in_month($$) { - my ($mon, $year) = @_; - return 28 + is_leap_year($year) if $mon == 1; - return (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$mon]; -} - -# day = 1.. -# mon = 0.. (Jan = 0) -sub wrap_time($) { - my ($time) = @_; - my ($sec, $min, $hour, $day, $mon, $year, $wday) = @$time; - - use integer; - if ($sec >= 60) { - $min += $sec / 60; - $sec %= 60; - } - - if ($min >= 60) { - $hour += $min / 60; - $min %= 60; - } - - if ($hour >= 24) { - $day += $hour / 24; - $wday += $hour / 24; - $hour %= 24; - } - - # Translate to 0..($days_in_mon-1) - --$day; - while (1) { - my $days_in_mon = days_in_month($mon % 12, $year); - last if $day < $days_in_mon; - # Wrap one month - $day -= $days_in_mon; - ++$mon; - } - # Translate back to 1..$days_in_mon - ++$day; - - if ($mon >= 12) { - $year += $mon / 12; - $mon %= 12; - } - - $wday %= 7; - return [$sec, $min, $hour, $day, $mon, $year, $wday]; -} - -# helper as we need to keep weekdays in sync -sub time_add_days($$) { - my ($time, $inc) = @_; - my ($sec, $min, $hour, $day, $mon, $year, $wday) = @$time; - return wrap_time([$sec, $min, $hour, $day + $inc, $mon, $year, $wday + $inc]); + return PVE::RS::CalendarEvent->new($event); } sub compute_next_event { my ($calspec, $last) = @_; - my $hspec = $calspec->{h}; - my $mspec = $calspec->{m}; - my $dowspec = $calspec->{dow}; - my $utc = $calspec->{utc}; - - $last += 60; # at least one minute later - - my $t = [$utc ? gmtime($last) : localtime($last)]; - $t->[0] = 0; # we're not interested in seconds, actually - $t->[5] += 1900; # real years for clarity - - outer: for (my $i = 0; $i < 1000; ++$i) { - my $wday = $t->[6]; - foreach my $d (@$dowspec) { - goto this_wday if $d == $wday; - if ($d > $wday) { - $t->[0] = $t->[1] = $t->[2] = 0; # sec = min = hour = 0 - $t = time_add_days($t, $d - $wday); - next outer; - } - } - # Test next week: - $t->[0] = $t->[1] = $t->[2] = 0; # sec = min = hour = 0 - $t = time_add_days($t, 7 - $wday); - next outer; - this_wday: - - goto this_hour if $hspec eq '*'; - my $hour = $t->[2]; - foreach my $h (@$hspec) { - goto this_hour if $h == $hour; - if ($h > $hour) { - $t->[0] = $t->[1] = 0; # sec = min = 0 - $t->[2] = $h; # hour = $h - next outer; - } - } - # Test next day: - $t->[0] = $t->[1] = $t->[2] = 0; # sec = min = hour = 0 - $t = time_add_days($t, 1); - next outer; - this_hour: - - goto this_min if $mspec eq '*'; - my $min = $t->[1]; - foreach my $m (@$mspec) { - goto this_min if $m == $min; - if ($m > $min) { - $t->[0] = 0; # sec = 0 - $t->[1] = $m; # min = $m - next outer; - } - } - # Test next hour: - $t->[0] = $t->[1] = 0; # sec = min = hour = 0 - $t->[2]++; - $t = wrap_time($t); - next outer; - this_min: - - return $utc ? timegm(@$t) : timelocal(@$t); - } - - die "unable to compute next calendar event\n"; + return $calspec->compute_next_event($last); } 1; diff --git a/test/calendar_event_test.pl b/test/calendar_event_test.pl index abbd74c..4572965 100755 --- a/test/calendar_event_test.pl +++ b/test/calendar_event_test.pl @@ -18,7 +18,7 @@ my $alldays = [0,1,2,3,4,5,6]; my $tests = [ [ '*', - { h => '*', m => '*', dow => $alldays }, + undef, [ [0, 60], [30, 60], @@ -28,7 +28,7 @@ my $tests = [ ], [ '*/10', - { h => '*', m => [0, 10, 20, 30, 40, 50], dow => $alldays }, + undef, [ [0, 600], [599, 600], @@ -38,7 +38,7 @@ my $tests = [ ], [ '*/12:0' , - { h => [0, 12], m => [0], dow => $alldays }, + undef, [ [ 10, 43200], [ 13*3600, 24*3600], @@ -46,7 +46,7 @@ my $tests = [ ], [ '1/12:0/15' , - { h => [1, 13], m => [0, 15, 30, 45], dow => $alldays }, + undef, [ [0, 3600], [3600, 3600+15*60], @@ -61,7 +61,7 @@ my $tests = [ ], [ '1,4,6', - { h => '*', m => [1, 4, 6], dow => $alldays}, + undef, [ [0, 60], [60, 4*60], @@ -71,15 +71,15 @@ my $tests = [ ], [ '0..3', - { h => '*', m => [ 0, 1, 2, 3 ], dow => $alldays }, + undef, ], [ '23..23:0..3', - { h => [ 23 ], m => [ 0, 1, 2, 3 ], dow => $alldays }, + undef, ], [ 'Mon', - { h => [0], m => [0], dow => [1] }, + undef, [ [0, 4*86400], # Note: Epoch 0 is Thursday, 1. January 1970 [4*86400, 11*86400], @@ -88,7 +88,7 @@ my $tests = [ ], [ 'sat..sun', - { h => [0], m => [0], dow => [0, 6] }, + undef, [ [0, 2*86400], [2*86400, 3*86400], @@ -97,7 +97,7 @@ my $tests = [ ], [ 'sun..sat', - { h => [0], m => [0], dow => $alldays }, + undef, ], [ 'Fri..Mon', @@ -105,15 +105,15 @@ my $tests = [ ], [ 'wed,mon..tue,fri', - { h => [0], m => [0], dow => [ 1, 2, 3, 5] }, + undef, ], [ 'mon */15', - { h => '*', m => [0, 15, 30, 45], dow => [1]}, + undef, ], [ '22/1:0', - { h => [22, 23], m => [0], dow => $alldays }, + undef, [ [0, 22*60*60], [22*60*60, 23*60*60], @@ -122,7 +122,7 @@ my $tests = [ ], [ '*/2:*', - { h => [0,2,4,6,8,10,12,14,16,18,20,22], m => '*', dow => $alldays }, + undef, [ [0, 60], [60*60, 2*60*60], @@ -131,7 +131,7 @@ my $tests = [ ], [ '20..22:*/30', - { h => [20,21,22], m => [0,30], dow => $alldays }, + undef, [ [0, 20*60*60], [20*60*60, 20*60*60 + 30*60], @@ -164,7 +164,7 @@ my $tests = [ ], [ '0,1,3..5', - { h => '*', m => [0,1,3,4,5], dow => $alldays }, + undef, [ [0, 60], [60, 3*60], @@ -173,7 +173,7 @@ my $tests = [ ], [ '2,4:0,1,3..5', - { h => [2,4], m => [0,1,3,4,5], dow => $alldays }, + undef, [ [0, 2*60*60], [2*60*60 + 60, 2*60*60 + 3*60], @@ -185,18 +185,16 @@ my $tests = [ foreach my $test (@$tests) { my ($t, $expect, $nextsync) = @$test; + $expect //= {}; + my $timespec; eval { $timespec = PVE::CalendarEvent::parse_calendar_event($t); }; my $err = $@; - delete $timespec->{utc}; if ($expect->{error}) { chomp $err if $err; - $timespec = { error => $err } if $err; - is_deeply($timespec, $expect, "expect parse error on '$t' - $expect->{error}"); + ok(defined($err) == defined($expect->{error}), "parsing '$t' failed expectedly"); die "unable to execute nextsync tests" if $nextsync; - } else { - is_deeply($timespec, $expect, "parse '$t'"); } next if !$nextsync; -- 2.30.2