Skip to content

Commit f5a6266

Browse files
committed
fix: Support CONTACT after PROLOG on first connection
Reworked target responses caching for event handling Closes #851
1 parent 5b40e2d commit f5a6266

File tree

4 files changed

+112
-48
lines changed

4 files changed

+112
-48
lines changed

Changes

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ Revision history for GLPI agent
22

33
1.13 not yet released
44

5+
core:
6+
* fix #851: glpi-agent should also try to request CONTACT after GLPI 10+ answer on PROLOG
7+
* Reworked target responses caching for event handling
8+
59
inventory:
610
* On windows, don't cache system is not 64 bits for the service lifetime as this can
711
be the result of a failed WMI call at the service start.

lib/GLPI/Agent.pm

+80-34
Original file line numberDiff line numberDiff line change
@@ -203,24 +203,17 @@ sub terminate {
203203
if ($self->{current_task});
204204
}
205205

206-
sub runTarget {
207-
my ($self, $target, $responses_only) = @_;
206+
sub getContact {
207+
my ($self, $target, $plannedTasks) = @_;
208208

209-
if ($target->isType('local') || $target->isType('server')) {
210-
$self->{logger}->info("target $target->{id}: " . $target->getType() . " " . $target->getName());
211-
}
209+
my $response;
212210

213-
# the prolog/contact dialog must be done once for all tasks,
214-
# but only for server targets
215-
my ($response, $contact_response);
216-
my $client;
217-
my @plannedTasks = $target->plannedTasks();
218211
if ($target->isGlpiServer()) {
219212
GLPI::Agent::HTTP::Client::GLPI->require();
220213
return $self->{logger}->error("GLPI Protocol library can't be loaded")
221214
if $EVAL_ERROR;
222215

223-
$client = GLPI::Agent::HTTP::Client::GLPI->new(
216+
my $client = GLPI::Agent::HTTP::Client::GLPI->new(
224217
logger => $self->{logger},
225218
config => $self->{config},
226219
agentid => uuid_to_string($self->{agentid}),
@@ -236,7 +229,7 @@ sub runTarget {
236229
$httpd_conf{"httpd-port"} = $self->{server}->{port};
237230
}
238231

239-
my %enabled = map { lc($_) => 1 } @plannedTasks;
232+
my %enabled = map { lc($_) => 1 } @{$plannedTasks};
240233
my $contact = GLPI::Agent::Protocol::Contact->new(
241234
logger => $self->{logger},
242235
deviceid => $self->{deviceid},
@@ -306,14 +299,14 @@ sub runTarget {
306299
$self->{_disabled_remoteinventory} = delete $disabled{remoteinventory};
307300
# Never disable inventory if force option is used
308301
delete $disabled{inventory} if $self->{config}->{force};
309-
@plannedTasks = grep { ! exists($disabled{lc($_)}) } @plannedTasks;
302+
$response->{plannedTasks} = [ grep { ! exists($disabled{lc($_)}) } @{$plannedTasks} ];
310303
} elsif (!ref($disabled)) {
311304
$disabled = lc($disabled);
312305
# Only disable inventory if force option is not set
313306
if ($disabled ne "inventory" || !$self->{config}->{force}) {
314-
@plannedTasks = grep {
315-
lc($_) ne $disabled
316-
} @plannedTasks;
307+
$response->{plannedTasks} = [
308+
grep { lc($_) ne $disabled } @{$plannedTasks}
309+
];
317310
}
318311
}
319312
}
@@ -322,7 +315,7 @@ sub runTarget {
322315
# Handle tasks informations returned by server in CONTACT answer
323316
if (ref($tasks) eq "HASH") {
324317
# Only keep task server support for planned tasks
325-
foreach my $task (map { lc($_) } @plannedTasks) {
318+
foreach my $task (map { lc($_) } @{$plannedTasks}) {
326319
next unless ref($tasks->{$task}) eq 'HASH';
327320

328321
# Keep task supporting announced by server
@@ -354,15 +347,17 @@ sub runTarget {
354347
}
355348
}
356349
}
357-
358-
# Keep contact response
359-
$contact_response = $response;
360350
}
361351

362-
# By default, PROLOG request could be avoided when communicating with a GLPI server
363-
# But it still may be required if we detect server supports any task due to glpiinventory plugin
364-
if ($target->isType('server') && $target->doProlog()) {
352+
return $response;
353+
}
354+
355+
sub getProlog {
356+
my ($self, $target) = @_;
357+
358+
my $response;
365359

360+
if ($target->isType('server')) {
366361
return unless GLPI::Agent::HTTP::Client::OCS->require();
367362

368363
my $agentid;
@@ -372,7 +367,7 @@ sub runTarget {
372367
$agentid = uuid_to_string($self->{agentid})
373368
unless $target->isGlpiServer();
374369

375-
$client = GLPI::Agent::HTTP::Client::OCS->new(
370+
my $client = GLPI::Agent::HTTP::Client::OCS->new(
376371
logger => $self->{logger},
377372
config => $self->{config},
378373
agentid => $agentid,
@@ -401,7 +396,6 @@ sub runTarget {
401396
unless ($target->isGlpiServer()) {
402397
$self->{logger}->info("$target->{id} answer shows it supports GLPI Agent protocol");
403398
$target->isGlpiServer('true');
404-
return $self->runTarget($target) unless $response->expiration;
405399
}
406400
} else {
407401
# update target
@@ -413,20 +407,72 @@ sub runTarget {
413407
}
414408
}
415409

416-
# Used when running tasks after a taskrun event
417-
if ($responses_only) {
418-
return {
419-
contact => $contact_response,
420-
response => $response
421-
};
410+
return $response;
411+
}
412+
413+
sub runTarget {
414+
my ($self, $target, $responses_only) = @_;
415+
416+
if ($target->isType('local') || $target->isType('server')) {
417+
$self->{logger}->info("target $target->{id}: " . $target->getType() . " " . $target->getName());
418+
}
419+
420+
# the prolog/contact dialog must be done once for all tasks,
421+
# but only for server targets
422+
my @plannedTasks = $target->plannedTasks();
423+
my @requests = ();
424+
my $responses = {};
425+
push @requests, 'CONTACT' if $target->isGlpiServer();
426+
push @requests, 'PROLOG' if !@requests && $target->isType('server');
427+
my %requested = qw(CONTACT 0 PROLOG 0);
428+
429+
while (@requests) {
430+
my $request = shift @requests;
431+
next if $responses->{$request};
432+
433+
my $response;
434+
$requested{$request} = 1;
435+
436+
if ($request eq 'CONTACT') {
437+
438+
$response = $self->getContact($target, \@plannedTasks);
439+
# Still return on error
440+
return $response if $response && !ref($response);
441+
442+
# Update plannedTasks
443+
if (ref($response) && $response->{plannedTasks}) {
444+
my $plannedTasks = delete $response->{plannedTasks};
445+
@plannedTasks = @{$plannedTasks};
446+
}
447+
448+
# Check condition where we also need to request PROLOG after a CONTACT
449+
push @requests, 'PROLOG'
450+
if ref($response) && $target->doProlog() && !$requested{PROLOG};
451+
452+
# By default, PROLOG request could be avoided when communicating with a GLPI server
453+
# But it still may be required if we detect server supports any task due to glpiinventory plugin
454+
} elsif ($request eq 'PROLOG') {
455+
456+
$response = $self->getProlog($target, \@plannedTasks);
457+
# Still return on error
458+
return $response if $response && !ref($response);
459+
460+
push @requests, 'CONTACT'
461+
if ref($response) && $target->isGlpiServer() && !$requested{CONTACT};
462+
}
463+
464+
$responses->{$request} = $response if ref($response);
422465
}
423466

467+
# Used when running tasks after a taskrun event
468+
return $responses if $responses_only;
469+
424470
foreach my $name (@plannedTasks) {
425-
my $server_response = $response;
426-
if ($contact_response) {
471+
my $server_response = $responses->{PROLOG} // $responses->{CONTACT};
472+
if ($responses->{CONTACT}) {
427473
# Be sure to use expected response for task
428474
my $task_server = $target->getTaskServer($name) // 'glpi';
429-
$server_response = $contact_response
475+
$server_response = $responses->{CONTACT}
430476
if $task_server eq 'glpi';
431477
}
432478
eval {

lib/GLPI/Agent/Daemon.pm

+19-14
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ sub run {
117117

118118
# background mode: work on a targets list copy, but loop while
119119
# the list really exists so we can stop quickly when asked for
120-
my $responses;
121120
while ($self->getTargets()) {
122121
my $time = time();
123122

@@ -131,7 +130,8 @@ sub run {
131130

132131
if ($target->paused()) {
133132

134-
undef $responses;
133+
# Reset target responses
134+
$target->responses({});
135135

136136
# Leave immediately if we passed in terminate method
137137
last if $self->{_terminate};
@@ -140,20 +140,24 @@ sub run {
140140
# Always remove event from list
141141
$target->delEvent($event);
142142

143+
my $responses = $target->responses();
144+
143145
# Contact server if required and cache responses
144146
if ($event->taskrun) {
145-
if (!defined($responses)) {
146-
eval {
147-
$responses = $self->runTarget($target, "contact-only");
148-
};
147+
if ((!ref($responses) || !$responses->{CONTACT}) && $target->isGlpiServer()) {
148+
$responses->{CONTACT} = $self->getContact($target, [$target->plannedTasks()]);
149+
}
150+
if ((!ref($responses) || !$responses->{PROLOG}) && $target->isType('server') && $event->task =~ /^net(discovery|inventory)$/i) {
151+
$responses->{PROLOG} = $self->getProlog($target);
149152
}
150153
# Fail event on no expected response from server
151-
unless (ref($responses)) {
154+
unless (ref($responses) && (ref($responses->{CONTACT}) || ref($responses->{PROLOG}))) {
152155
$logger->error("Failed to handle run event for ".$event->task) if $logger && $event->task;
153156
next;
154157
}
155-
} else {
156-
undef $responses;
158+
159+
# Keep target responses
160+
$target->responses($responses);
157161
}
158162

159163
eval {
@@ -170,6 +174,9 @@ sub run {
170174
$target->setNextRunDateFromNow();
171175
$target->resetNextRunDate();
172176

177+
# This is also safe to reset target responses
178+
$target->responses({});
179+
173180
if ($logger) {
174181
my $date = $target->getFormatedNextRunDate();
175182
my $id = $target->id();
@@ -183,8 +190,6 @@ sub run {
183190

184191
} elsif ($time >= $target->getNextRunDate()) {
185192

186-
undef $responses;
187-
188193
my $net_error = 0;
189194
eval {
190195
$net_error = $self->runTarget($target);
@@ -275,11 +280,11 @@ sub runTargetEvent {
275280
$target->triggerRunTasksNow($event);
276281

277282
} elsif (ref($responses)) {
278-
my $server_response = $responses->{response};
279-
if ($responses->{contact}) {
283+
my $server_response = $responses->{PROLOG};
284+
if ($responses->{CONTACT}) {
280285
# Be sure to use expected response for task
281286
my $task_server = $target->getTaskServer($task) // 'glpi';
282-
$server_response = $responses->{contact}
287+
$server_response = $responses->{CONTACT}
283288
if $task_server eq 'glpi';
284289
}
285290
eval {

lib/GLPI/Agent/Target.pm

+9
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ sub triggerRunTasksNow {
215215

216216
$self->addEvent(GLPI::Agent::Event->new(%event), 1);
217217
}
218+
219+
# Also reset cached responses
220+
delete $self->{_responses};
218221
}
219222

220223
sub addEvent {
@@ -461,6 +464,12 @@ sub getTaskVersion {
461464
return $self->{_glpi};
462465
}
463466

467+
sub responses {
468+
my ($self, $responses) = @_;
469+
return $self->{_responses} unless defined($responses);
470+
$self->{_responses} = $responses;
471+
}
472+
464473
1;
465474
__END__
466475

0 commit comments

Comments
 (0)