1package FusionInventory::Agent::Target;
2
3use strict;
4use warnings;
5
6use English qw(-no_match_vars);
7
8use FusionInventory::Agent::Logger;
9use FusionInventory::Agent::Storage;
10
11my $errMaxDelay = 0;
12
13sub new {
14    my ($class, %params) = @_;
15
16    die "no basevardir parameter for target\n" unless $params{basevardir};
17
18    # errMaxDelay is the maximum delay on network error. Delay on network error starts
19    # from 60, is doubled at each new failed attempt until reaching delaytime.
20    # Take the first provided delaytime for the agent lifetime
21    unless ($errMaxDelay) {
22        $errMaxDelay = $params{delaytime} || 3600;
23    }
24
25    my $self = {
26        logger       => $params{logger} ||
27                        FusionInventory::Agent::Logger->new(),
28        maxDelay     => $params{maxDelay} || 3600,
29        errMaxDelay  => $errMaxDelay,
30        initialDelay => $params{delaytime},
31    };
32    bless $self, $class;
33
34    return $self;
35}
36
37sub _init {
38    my ($self, %params) = @_;
39
40    my $logger = $self->{logger};
41
42    # target identity
43    $self->{id} = $params{id};
44
45    $self->{storage} = FusionInventory::Agent::Storage->new(
46        logger    => $self->{logger},
47        directory => $params{vardir}
48    );
49
50    # handle persistent state
51    $self->_loadState();
52
53    $self->{nextRunDate} = $self->_computeNextRunDate()
54        if (!$self->{nextRunDate} || $self->{nextRunDate} < time-$self->getMaxDelay());
55
56    $self->_saveState();
57
58    $logger->debug(
59        "[target $self->{id}] Next server contact planned for " .
60        localtime($self->{nextRunDate})
61    );
62
63}
64
65sub getStorage {
66    my ($self) = @_;
67
68    return $self->{storage};
69}
70
71sub setNextRunDateFromNow {
72    my ($self, $nextRunDelay) = @_;
73
74    if ($nextRunDelay) {
75        # While using nextRunDelay, we double it on each consecutive call until
76        # delay reach target defined maxDelay. This is only used on network failure.
77        $nextRunDelay = 2 * $self->{_nextrundelay} if ($self->{_nextrundelay});
78        $nextRunDelay = $self->getMaxDelay() if ($nextRunDelay > $self->getMaxDelay());
79        # Also limit toward the initial delaytime as it is also used to
80        # define the maximum delay on network error
81        $nextRunDelay = $self->{errMaxDelay} if ($nextRunDelay > $self->{errMaxDelay});
82        $self->{_nextrundelay} = $nextRunDelay;
83    }
84    $self->{nextRunDate} = time + ($nextRunDelay || 0);
85    $self->_saveState();
86
87    # Remove initialDelay to support case we are still forced to run at start
88    $self->{initialDelay} = undef;
89}
90
91sub resetNextRunDate {
92    my ($self) = @_;
93
94    $self->{_nextrundelay} = 0;
95    $self->{nextRunDate} = $self->_computeNextRunDate();
96    $self->_saveState();
97}
98
99sub getNextRunDate {
100    my ($self) = @_;
101
102    # Check if state file has been updated by a third party, like a script run
103    $self->_loadState() if $self->_needToReloadState();
104
105    return $self->{nextRunDate};
106}
107
108sub paused {
109    my ($self) = @_;
110
111    return $self->{_paused} || 0;
112}
113
114sub pause {
115    my ($self) = @_;
116
117    $self->{_paused} = 1;
118}
119
120sub continue {
121    my ($self) = @_;
122
123    delete $self->{_paused};
124}
125
126sub getFormatedNextRunDate {
127    my ($self) = @_;
128
129    return $self->{nextRunDate} > 1 ?
130        scalar localtime($self->{nextRunDate}) : "now";
131}
132
133sub getMaxDelay {
134    my ($self) = @_;
135
136    return $self->{maxDelay};
137}
138
139sub setMaxDelay {
140    my ($self, $maxDelay) = @_;
141
142    $self->{maxDelay} = $maxDelay;
143    $self->_saveState();
144}
145
146sub isType {
147    my ($self, $testtype) = @_;
148
149    return unless $testtype;
150
151    my $type = $self->getType()
152        or return;
153
154    return "$type" eq "$testtype";
155}
156
157# compute a run date, as current date and a random delay
158# between maxDelay / 2 and maxDelay
159sub _computeNextRunDate {
160    my ($self) = @_;
161
162    my $ret;
163    if ($self->{initialDelay}) {
164        $ret = time + ($self->{initialDelay} / 2) + int rand($self->{initialDelay} / 2);
165        $self->{initialDelay} = undef;
166    } else {
167        # By default, reduce randomly the delay by 0 to 3600 seconds (1 hour max)
168        my $max_random_delay_reduc = 3600;
169        # For delays until 6 hours, reduce randomly the delay by 10 minutes for each hour: 600*(T/3600) = T/6
170        if ($self->{maxDelay} < 21600) {
171            $max_random_delay_reduc = $self->{maxDelay} / 6;
172        } elsif ($self->{maxDelay} > 86400) {
173            # Finally reduce randomly the delay by 1 hour for each 24 hours, for delay other than a day
174            $max_random_delay_reduc = $self->{maxDelay} / 24;
175        }
176        $ret = time + $self->{maxDelay} - int(rand($max_random_delay_reduc));
177    }
178
179    return $ret;
180}
181
182sub _loadState {
183    my ($self) = @_;
184
185    my $data = $self->{storage}->restore(name => 'target');
186
187    $self->{maxDelay}    = $data->{maxDelay}    if $data->{maxDelay};
188    $self->{nextRunDate} = $data->{nextRunDate} if $data->{nextRunDate};
189}
190
191sub _saveState {
192    my ($self) = @_;
193
194    $self->{storage}->save(
195        name => 'target',
196        data => {
197            maxDelay    => $self->{maxDelay},
198            nextRunDate => $self->{nextRunDate},
199        }
200    );
201}
202
203sub _needToReloadState {
204    my ($self) = @_;
205
206    # Only re-check if it's time to reload after 30 seconds
207    return if $self->{_next_reload_check} && time < $self->{_next_reload_check};
208
209    $self->{_next_reload_check} = time+30;
210
211    return $self->{storage}->modified(name => 'target');
212}
213
2141;
215__END__
216
217=head1 NAME
218
219FusionInventory::Agent::Target - Abstract target
220
221=head1 DESCRIPTION
222
223This is an abstract class for execution targets.
224
225=head1 METHODS
226
227=head2 new(%params)
228
229The constructor. The following parameters are allowed, as keys of the %params
230hash:
231
232=over
233
234=item I<logger>
235
236the logger object to use
237
238=item I<maxDelay>
239
240the maximum delay before contacting the target, in seconds
241(default: 3600)
242
243=item I<basevardir>
244
245the base directory of the storage area (mandatory)
246
247=back
248
249=head2 getNextRunDate()
250
251Get nextRunDate attribute.
252
253=head2 getFormatedNextRunDate()
254
255Get nextRunDate attribute as a formated string.
256
257=head2 setNextRunDateFromNow($nextRunDelay)
258
259Set next execution date from now and after $nextRunDelay seconds (0 by default).
260
261=head2 resetNextRunDate()
262
263Set next execution date to a random value.
264
265=head2 getMaxDelay($maxDelay)
266
267Get maxDelay attribute.
268
269=head2 setMaxDelay($maxDelay)
270
271Set maxDelay attribute.
272
273=head2 getStorage()
274
275Return the storage object for this target.
276