1 |
rodolico |
1 |
#! /usr/bin/perl -w
|
|
|
2 |
|
|
|
3 |
# process_sysinfo.pl
|
|
|
4 |
# Author: R. W. Rodolico
|
|
|
5 |
# Part of sysinfo package. This application takes the output from sysinfo.pl as input, parsing
|
|
|
6 |
# it and populating a database (see sysinfo.sql) with the results.
|
|
|
7 |
# Application will bypass anything up to the line beginning with [sysinfo version], allowing
|
|
|
8 |
# the output of sysinfo.pl to be e-mailed to a central server, where this app will correctly parse
|
|
|
9 |
# it.
|
|
|
10 |
# Application also has limited sensing capabilities, and will respond to certain conditions with a
|
|
|
11 |
# message on STDOUT indicating conditions, such as disk usage above an alarm value, new Operating Systems
|
|
|
12 |
# installed, etc... The goal is to allow this program to be called by a cron job, and the result (if any)
|
|
|
13 |
# returned in the cron specified e-mail.
|
|
|
14 |
# OVERVIEW:
|
|
|
15 |
# The input file contains two types of information, both beginning with a tag surrounded by square
|
|
|
16 |
# brackets. The single line data has the value for that tag immediately following the tag, ie:
|
|
|
17 |
# [tagname]value
|
|
|
18 |
# Multiline data has a beginning tag, then data following on a line by line basis, until the next
|
|
|
19 |
# tag (denoted by a tag name surrounded by square brackets) is reached. Every line between the first
|
|
|
20 |
# and subsequent tag are considered data for the previous tag, ie:
|
|
|
21 |
# [tagname1]
|
|
|
22 |
# value 1
|
|
|
23 |
# value 2
|
|
|
24 |
# [tagname2]
|
|
|
25 |
# thus, value 1, and value 2 are data for tagname1. Multiline data is stored in its own hash, and handled
|
|
|
26 |
# by separate routines.
|
|
|
27 |
# See bottom of this app for main routine; subroutines are in between here and the beginning of the main
|
|
|
28 |
# code
|
|
|
29 |
|
|
|
30 |
# Required Libraries:
|
|
|
31 |
# GenericSQL - Home Grown MySQL access routine, included with package
|
|
|
32 |
# GenericTemplates - Home Grown routine, included with package
|
|
|
33 |
# Logging - Home Grown logging routine, included with package
|
|
|
34 |
# Date (uses format and parse)
|
|
|
35 |
|
|
|
36 |
# Suggested Uses: See process_sysinfo.sh. This is an example that looks for all messages in a directory
|
|
|
37 |
# (in this case, a maildir directory), processes each e-mail, then moves the e-mail
|
|
|
38 |
# to the Processed folder. The advantage to this is that it can be called by a nightly
|
|
|
39 |
# cron job, with any warnings e-mailed back to the sysadmin
|
|
|
40 |
|
|
|
41 |
# version 0.10 20071103
|
|
|
42 |
# Ready for distribution. Database fairly normalized.
|
|
|
43 |
# version 0.11 20071104
|
|
|
44 |
# Bug fix, and set up for "uninstalled" software
|
|
|
45 |
# version 0.12 20071104
|
|
|
46 |
# Adjusted so it will ignore blank package lines, and will handle directories_to_watch
|
|
|
47 |
# version 0.13 20071120
|
|
|
48 |
# Fixed problem where a file that was NOT a valid sysinfo file would cause a run-away
|
|
|
49 |
# version 1.00 20071206
|
|
|
50 |
# Modified for new database format
|
|
|
51 |
# version 1.01 20071208
|
|
|
52 |
# Modified for report_date to be a date/time stamp instead of just the date
|
|
|
53 |
# version 2.0.0b 20081208
|
|
|
54 |
# converted to read XML data. Also uses older style data, but converts it to standard hash to mimic xml
|
|
|
55 |
# Adds requirement for libxml-simple-perl
|
|
|
56 |
# version 2.0.1 20090416
|
|
|
57 |
# Bug fix
|
|
|
58 |
# version 3.0.0 20121002
|
|
|
59 |
# completely changed i/o format. NOT BACKWARDS COMPATIBLE
|
|
|
60 |
# this will NOT read v2 or 1 files. It will only read v3.x files
|
|
|
61 |
# and greater. Uses YAML for data input
|
|
|
62 |
#
|
|
|
63 |
# YOU MUST have perl module YAML::XS installed
|
|
|
64 |
# Under Debian:
|
|
|
65 |
# apt-get install libyaml-libyaml-perl
|
|
|
66 |
|
|
|
67 |
|
|
|
68 |
my $VERSION = '3.0.0';
|
|
|
69 |
|
|
|
70 |
# only required if reports are sent from process_sysinfo. Not the norm.
|
|
|
71 |
my $iMailResults;
|
|
|
72 |
my $mailTo;
|
|
|
73 |
my $mailCC;
|
|
|
74 |
my $mailBCC;
|
|
|
75 |
my $mailServer;
|
|
|
76 |
my $mailServerPort;
|
|
|
77 |
my $mailFrom;
|
|
|
78 |
my $SENDMAIL;
|
|
|
79 |
my $DiskUsageAlert = 90; # will generate a warning if any disk has more than this percent capacity used
|
|
|
80 |
|
|
|
81 |
# information for the database
|
|
|
82 |
#my $DSN = 'DBI:mysql:camp'; # and, set up the database access
|
|
|
83 |
#my $DB_USER = 'test';
|
|
|
84 |
#my $DB_PASS = 'test';
|
|
|
85 |
|
|
|
86 |
#my $LIBRARIES = '/home/www/common-cgi/'; # where the extra libraries are stored
|
|
|
87 |
|
|
|
88 |
# global variables (not configuration information)
|
|
|
89 |
my $dbh; # global variable for database handle
|
|
|
90 |
my @warnings; # variable to hold errors and warnings
|
|
|
91 |
my $FATAL = ''; # in case of a fatal error, this variable will hold the reason
|
|
|
92 |
|
|
|
93 |
|
|
|
94 |
# Globals that hold information after parsing by readAndParseInput
|
|
|
95 |
my $datafileVersion;# store the data file version here.
|
|
|
96 |
my $reportDate; # global for the report date
|
|
|
97 |
my $clientID;
|
|
|
98 |
my $computerID;
|
|
|
99 |
my $clientName;
|
|
|
100 |
my $computerName;
|
|
|
101 |
|
|
|
102 |
|
|
|
103 |
|
|
|
104 |
#my %info; # stores any single line tag/value pair
|
|
|
105 |
#my $ipAddresses; # stores IP's for this machine
|
|
|
106 |
#my @diskInfo; # stores the disk info
|
|
|
107 |
#my @packages; # stores package info
|
|
|
108 |
#my @directoriesToWatch; # stores directories to watch
|
|
|
109 |
#my @pciInfo; # stores pci info
|
|
|
110 |
|
|
|
111 |
|
|
|
112 |
# safe check for equality. Handles undefined
|
|
|
113 |
sub checkEquals {
|
|
|
114 |
my ($first, $second) = @_;
|
|
|
115 |
return ($first eq $second) if (defined $first) and (defined $second);
|
|
|
116 |
return 1 if (! defined $first) and (! defined $second); # both undefined, so equal
|
|
|
117 |
return 0;
|
|
|
118 |
}
|
|
|
119 |
|
|
|
120 |
# standard find and load configuration file
|
|
|
121 |
sub loadConfigurationFile {
|
|
|
122 |
my $configuration_file = shift;
|
|
|
123 |
|
|
|
124 |
use File::Basename;
|
|
|
125 |
use Cwd qw(realpath);
|
|
|
126 |
|
|
|
127 |
my $filename = realpath($0); # get my real path
|
|
|
128 |
my $directories;
|
|
|
129 |
my $suffix;
|
|
|
130 |
#print "$configuration_file\n";
|
|
|
131 |
$configuration_file = $filename unless $configuration_file;
|
|
|
132 |
#print "$configuration_file\n";
|
|
|
133 |
|
|
|
134 |
if ( $configuration_file !~ m/\// ) { # no path information
|
|
|
135 |
($filename, $directories, $suffix) = fileparse($filename,qr/\.[^.]*/); # break filename apart
|
|
|
136 |
#print "No Path Given\n";
|
|
|
137 |
} else {
|
|
|
138 |
($filename, $directories, $suffix) = fileparse($configuration_file,qr/\.[^.]*/); # break filename apart
|
|
|
139 |
$configuration_file = '';
|
|
|
140 |
#print "Path included\n";
|
|
|
141 |
}
|
|
|
142 |
unless (-e $directories . ($configuration_file ? $configuration_file : $filename) . '.conf' ) {
|
|
|
143 |
$lookingIn = $directories;
|
|
|
144 |
while ($lookingIn) {
|
|
|
145 |
$lookingIn =~ m/^(.*\/)[^\/]+\//;
|
|
|
146 |
$lookingIn = $1;
|
|
|
147 |
#print "$lookingIn\n";
|
|
|
148 |
if (-e $lookingIn . ($configuration_file ? $configuration_file : $filename) . '.conf' ) {
|
|
|
149 |
$directories = $lookingIn;
|
|
|
150 |
$lookingIn = '';
|
|
|
151 |
}
|
|
|
152 |
}
|
|
|
153 |
}
|
|
|
154 |
$configuration_file = $directories . ($configuration_file ? $configuration_file : $filename) . '.conf'; # add the .conf
|
|
|
155 |
open CONFFILE, "<$configuration_file" or die "Can not open configuration file $configuration_file";
|
|
|
156 |
my $confFileContents = join( '', <CONFFILE> );
|
|
|
157 |
close CONFFILE;
|
|
|
158 |
return $confFileContents;
|
|
|
159 |
}
|
|
|
160 |
|
|
|
161 |
# just a nice place to format any warnings/errors. Just prepend the client and computer name
|
|
|
162 |
sub createLogMessage {
|
|
|
163 |
my $message = shift;
|
|
|
164 |
$message = "$clientName - $computerName: " . $message;
|
|
|
165 |
return $message;
|
|
|
166 |
}
|
|
|
167 |
|
|
|
168 |
# generic routine to send an e-mail
|
|
|
169 |
sub sendmessage {
|
|
|
170 |
my ( $from, $to, $subject, $message, $cc, $bcc, $server, $port ) = @_;
|
|
|
171 |
|
|
|
172 |
open SENDMAIL, "|$SENDMAIL" or die "Could not open sendmail";
|
|
|
173 |
print SENDMAIL "From: $from\nTo: $to\nSubject: $subject\n";
|
|
|
174 |
print SENDMAIL "cc: $cc\n" if $cc;
|
|
|
175 |
print SENDMAIL "bcc: $bcc\n" if $bcc;
|
|
|
176 |
print SENDMAIL "$message\n";
|
|
|
177 |
print SENDMAIL ".\n";
|
|
|
178 |
close SENDMAIL;
|
|
|
179 |
}
|
|
|
180 |
|
|
|
181 |
# simply used to get an attrib_id. If it does not exit, will create it
|
|
|
182 |
|
|
|
183 |
sub getAttributeID {
|
|
|
184 |
my ($attributeName ) = @_;
|
|
|
185 |
my $sql = qq/select attrib_id from attrib where name = $attributeName and removed_date is null/;
|
|
|
186 |
my $result = &GenericSQL::getOneValue($dbh,$sql);
|
|
|
187 |
unless ( $result ) {
|
|
|
188 |
my $insertSQL = qq/insert into attrib (name,added_date) values ($attributeName,$reportDate)/;
|
|
|
189 |
&GenericSQL::doSQL( $dbh,$insertSQL );
|
|
|
190 |
$result = &GenericSQL::getOneValue($dbh,$sql);
|
|
|
191 |
push @warnings, "Added a new attribute type [$attributeName]";
|
|
|
192 |
}
|
|
|
193 |
return $result;
|
|
|
194 |
}
|
|
|
195 |
|
|
|
196 |
sub fixDatabaseValue {
|
|
|
197 |
# just return NULL if the parameter is invalid
|
|
|
198 |
return 'NULL' unless defined $_[0];
|
|
|
199 |
|
|
|
200 |
my ($value,$alwaysQuote) = @_;
|
|
|
201 |
# remove leading and trailing blank spaces
|
|
|
202 |
$value =~ s/^ +//gi;
|
|
|
203 |
$value =~ s/ +$//gi;
|
|
|
204 |
if ($alwaysQuote or ($value !~ m/^\d+$/)) { # Not a numeric value
|
|
|
205 |
$value = &GenericSQL::fixStringValue($dbh, $value); # so get it ready for SQL (ie, put quotes around it, etc...
|
|
|
206 |
}
|
|
|
207 |
#$value = "'$value'" if $alwaysQuote && $value ;
|
|
|
208 |
#print "AlwaysQuote [$alwaysQuote], value [$value]\n";
|
|
|
209 |
return $value;
|
|
|
210 |
}
|
|
|
211 |
|
|
|
212 |
sub checkAndUpdateAttribute {
|
|
|
213 |
my ($ID,$attribute,$value ) = @_;
|
|
|
214 |
unless ($attribute && $value) {
|
|
|
215 |
push @warnings, "Error: attempt to use null value for [$attribute], value [$value] for ID $ID in checkAndUPdateAttribute";
|
|
|
216 |
return 0;
|
|
|
217 |
}
|
|
|
218 |
$value = &fixDatabaseValue($value, 1); # we want to always quote the value on this particular one
|
|
|
219 |
#print "\tcheckAndUpdateAttribute:attribute/value = [$attribute][$value]\n";
|
|
|
220 |
$attribute = &fixDatabaseValue( $attribute );
|
|
|
221 |
my $attrib_id = &getAttributeID( $attribute, $reportDate );
|
|
|
222 |
my $sql = qq/
|
|
|
223 |
select device_attrib.value
|
|
|
224 |
from device_attrib join attrib using (attrib_id)
|
|
|
225 |
where device_attrib.device_id = $ID
|
|
|
226 |
and device_attrib.removed_date is null
|
|
|
227 |
and attrib.attrib_id = $attrib_id
|
|
|
228 |
/;
|
|
|
229 |
$result = &GenericSQL::getOneValue($dbh,$sql);
|
|
|
230 |
$result = &fixDatabaseValue( $result, 1 ); # we must do this since we are comparing to $value which has had this done
|
|
|
231 |
if ( $result ) { # got it, now see if it compares ok
|
|
|
232 |
if ( $value ne $result ) { # nope, this has changed. Note, must use fixDatabaseValue for escapes already in $value
|
|
|
233 |
# first, set the removed_date to now on the old part
|
|
|
234 |
#die "[$reportDate][$ID][$attrib_id]\n";
|
|
|
235 |
#print "\tresult = [$result], value = [$value]\n";
|
|
|
236 |
$sql = qq/
|
|
|
237 |
update device_attrib
|
|
|
238 |
set removed_date = $reportDate
|
|
|
239 |
where device_id = $ID
|
|
|
240 |
and attrib_id = $attrib_id
|
|
|
241 |
and removed_date is null
|
|
|
242 |
/;
|
|
|
243 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
244 |
undef $result; # this will force the insert in the next block of code
|
|
|
245 |
} # if $result ne $value
|
|
|
246 |
} # if ($result)
|
|
|
247 |
unless ( $result ) { # we have no valid entry for this attribute
|
|
|
248 |
$sql = qq/
|
|
|
249 |
insert into device_attrib(device_id,attrib_id,value,added_date)
|
|
|
250 |
values ($ID,$attrib_id,$value,$reportDate)
|
|
|
251 |
/;
|
|
|
252 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
253 |
return 1;
|
|
|
254 |
}
|
|
|
255 |
return 0;
|
|
|
256 |
}
|
|
|
257 |
|
|
|
258 |
sub ReadAndParseYAML {
|
|
|
259 |
use YAML::XS;
|
|
|
260 |
my ( $line, $version ) = @_;
|
|
|
261 |
$datafileVersion = $version; # update the global variable
|
|
|
262 |
# get the rest of the file into memory for processing
|
|
|
263 |
# it is possible that the line contains some trailing trash, so we assume
|
|
|
264 |
# if the originating program was nice, it put an ellipses at the end (...)
|
|
|
265 |
# thus, we read the entire file into memory UNLESS we are stopped earlier
|
|
|
266 |
# by a line which has three "dots" at the absolute beginning and nothing else
|
|
|
267 |
# the line
|
|
|
268 |
# NOTE: for some reason, some mailers will reduce ... to .., so we're treating ..
|
|
|
269 |
# followed by any number of spaces and dots the same. THIS IS NOT valid YAML, but
|
|
|
270 |
# you could, in theory, end the line with ^.... ....... .....$ where the ^
|
|
|
271 |
# indicates beginning of line and the $ indicates the end of the line
|
|
|
272 |
while ( my $temp = <STDIN> ) {
|
|
|
273 |
$line .= $temp;
|
|
|
274 |
last if $temp =~ m/^\.\.[ .]*$/; # found YAML end of document marker
|
|
|
275 |
}
|
|
|
276 |
return Load( $line );
|
|
|
277 |
}
|
|
|
278 |
|
|
|
279 |
# main subroutine that reads and parses the input
|
|
|
280 |
# it will place the appropriate values into the arrays/hashes
|
|
|
281 |
sub readAndParseInput {
|
|
|
282 |
while ( $line = <STDIN> ) {
|
|
|
283 |
if ( $line =~ m/^#\s*sysinfo:\s*([0-9.]+)\s*yaml/i ) { # YAML document with leading comment
|
|
|
284 |
return &ReadAndParseYAML( $line, $1 );
|
|
|
285 |
}
|
|
|
286 |
}
|
|
|
287 |
# if we reach this point, we never found the correct header, so it is a format we don't recognize
|
|
|
288 |
# so, set error code and just return nothing
|
|
|
289 |
$FATAL = 'Invalid Report File';
|
|
|
290 |
return;
|
|
|
291 |
}
|
|
|
292 |
|
|
|
293 |
# tries to figure out the client. If the client does not exist, will create a null record
|
|
|
294 |
# for them. Stores result in $client_id (reading $clientName)
|
|
|
295 |
sub getClientID {
|
|
|
296 |
# let's see if the client exists
|
|
|
297 |
$client = &fixDatabaseValue($clientName);
|
|
|
298 |
$sql = qq/select client_id from client where name = $client and removed_date is null/;
|
|
|
299 |
my $client_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
300 |
unless ($client_id) { # no entry, check the alias table
|
|
|
301 |
$sql = qq/select client_id from client_alias where alias=$client and removed_date is null/;
|
|
|
302 |
$client_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
303 |
}
|
|
|
304 |
# the following has been changed to simply return a message
|
|
|
305 |
unless ( $client_id ) { # nope, client does not exist, so add them
|
|
|
306 |
$device = &fixDatabaseValue($computerName);
|
|
|
307 |
$sql = qq/select report_date from unknown_entry where client_name = $client and device_name = $device/;
|
|
|
308 |
#$report = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
309 |
#print STDERR "Report Date $report\n";
|
|
|
310 |
if ($report = &GenericSQL::getOneValue( $dbh, $sql )) {
|
|
|
311 |
$FATAL = "New Client detected, but entry already made. You must update Camp before this can be processed";
|
|
|
312 |
} else {
|
|
|
313 |
$sql = qq/insert into unknown_entry(client_name,device_name,report_date) values ($client, $device, $reportDate)/;
|
|
|
314 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
315 |
$FATAL = "Warning, new client $client found with new device $device. You must update Camp before this report can be processed\n";
|
|
|
316 |
}
|
|
|
317 |
}
|
|
|
318 |
return $client_id;
|
|
|
319 |
}
|
|
|
320 |
|
|
|
321 |
# get a device type id from the device table. Create it if it does not exist
|
|
|
322 |
sub getDeviceTypeID {
|
|
|
323 |
my $typeDescription = &GenericSQL::fixStringValue( $dbh, shift );
|
|
|
324 |
my $reportDate = shift;
|
|
|
325 |
$sql = qq/select device_type_id from device_type where name = $typeDescription and removed_date is null/;
|
|
|
326 |
my $id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
327 |
unless ($id) {
|
|
|
328 |
my $sql_insert = qq/insert into device_type ( name,added_date ) values ($typeDescription, $reportDate) /;
|
|
|
329 |
&GenericSQL::doSQL( $dbh, $sql_insert );
|
|
|
330 |
$id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
331 |
}
|
|
|
332 |
return $id;
|
|
|
333 |
}
|
|
|
334 |
|
|
|
335 |
# gets the computer ID. If the computerID does not exist, creates it.
|
|
|
336 |
# returns the id of the computer.
|
|
|
337 |
sub getComputerID {
|
|
|
338 |
# ok, does this computer name exist (each computer name per site must be unique)
|
|
|
339 |
$computer = &fixDatabaseValue($computerName);
|
|
|
340 |
my $sql = qq/
|
|
|
341 |
select device_id
|
|
|
342 |
from device join site using (site_id)
|
|
|
343 |
where site.client_id = $clientID
|
|
|
344 |
and device.name = $computer
|
|
|
345 |
and device.removed_date is null
|
|
|
346 |
/;
|
|
|
347 |
my $computer_id = &GenericSQL::getOneValue( $dbh, $sql ); # actually, result of query above
|
|
|
348 |
unless ( $computer_id ) { # didn't find it. Let's see if it is in the alias table
|
|
|
349 |
$sql = qq/select device_id from device_alias join device using (device_id) join site using (site_id) where device_alias.alias = $computer and site.client_id = $clientID/;
|
|
|
350 |
$computer_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
351 |
}
|
|
|
352 |
# changed to just give a warning
|
|
|
353 |
unless ( $computer_id ) { # nope, computer does not exist so create it
|
|
|
354 |
$client = &fixDatabaseValue($clientName);
|
|
|
355 |
$sql = qq/select report_date from unknown_entry where client_name = $client and device_name = $computer and processed_date is null/;
|
|
|
356 |
#$report = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
357 |
#print STDERR "Report Date $report\n";
|
|
|
358 |
if ($report = &GenericSQL::getOneValue( $dbh, $sql )) {
|
|
|
359 |
$FATAL = "New Device detected, but entry already made. You must update Camp before this can be processed";
|
|
|
360 |
} else {
|
|
|
361 |
$sql = qq/insert into unknown_entry(client_name,device_name,report_date) values ($client, $computer, $reportDate)/;
|
|
|
362 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
363 |
$FATAL = "Warning, new device $computer found associated with client $client. You must update Camp before this report can be processed\n";
|
|
|
364 |
}
|
|
|
365 |
}
|
|
|
366 |
return $computer_id;
|
|
|
367 |
}
|
|
|
368 |
|
|
|
369 |
# checks for a duplicate report, ie one that has already been run.
|
|
|
370 |
# the only thing that always changes is disk space usage, so just look to see
|
|
|
371 |
# if this computer has a report already for this date/time
|
|
|
372 |
sub recordReport {
|
|
|
373 |
my $sql = qq/select count(*) from sysinfo_report where device_id = $computerID and report_date = $reportDate/;
|
|
|
374 |
if (&GenericSQL::getOneValue( $dbh, $sql ) > 0) {
|
|
|
375 |
my $thisDevice = &GenericSQL::getOneValue( $dbh, "select name from device where device_id = $computerID" );
|
|
|
376 |
$FATAL = qq/Duplicate Report for $thisDevice (id $computerID) on $reportDate/;
|
|
|
377 |
return;
|
|
|
378 |
}
|
|
|
379 |
$version = &fixDatabaseValue($datafileVersion);
|
|
|
380 |
# if we made it this far, we are ok, so just add the report id
|
|
|
381 |
$sql = qq/insert into sysinfo_report(device_id,version,report_date,added_date) values ($computerID,$version,$reportDate,now())/;
|
|
|
382 |
&GenericSQL::doSQL($dbh,$sql);
|
|
|
383 |
$sql = qq/select sysinfo_report_id from sysinfo_report where device_id = $computerID and report_date = $reportDate/;
|
|
|
384 |
return &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
385 |
}
|
|
|
386 |
|
|
|
387 |
# gets operating system ID. If it does not exist, creates it
|
|
|
388 |
sub getOSID {
|
|
|
389 |
my ($osHash) = shift;
|
|
|
390 |
my $os_id;
|
|
|
391 |
my $osName = &fixDatabaseValue($$osHash{'os_name'});
|
|
|
392 |
my $kernel = &fixDatabaseValue($$osHash{'kernel'});
|
|
|
393 |
my $distro_name = &fixDatabaseValue($$osHash{'distribution'});
|
|
|
394 |
my $release = &fixDatabaseValue($$osHash{'release'});
|
|
|
395 |
my $version = &fixDatabaseValue($$osHash{'os_version'});
|
|
|
396 |
my $description = &fixDatabaseValue($$osHash{'description'});
|
|
|
397 |
my $codename = &fixDatabaseValue($$osHash{'codename'});
|
|
|
398 |
|
|
|
399 |
$sql = qq/select operating_system_id from operating_system
|
|
|
400 |
where name = $osName
|
|
|
401 |
and kernel = $kernel
|
|
|
402 |
and distro = $distro_name
|
|
|
403 |
and distro_release = $release /;
|
|
|
404 |
unless ( $os_id = &GenericSQL::getOneValue( $dbh, $sql ) ) {
|
|
|
405 |
$sql = qq/insert into operating_system (name,version,kernel,distro,distro_description,distro_release,distro_codename, added_date) values
|
|
|
406 |
($osName,$version,$kernel,$distro_name,$description,$release,$codename, $reportDate)/;
|
|
|
407 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
408 |
$sql = qq/select operating_system_id from operating_system
|
|
|
409 |
where name = $osName
|
|
|
410 |
and kernel = $kernel
|
|
|
411 |
and distro = $distro_name
|
|
|
412 |
and distro_release = $release /;
|
|
|
413 |
$os_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
414 |
}
|
|
|
415 |
return $os_id;
|
|
|
416 |
}
|
|
|
417 |
|
|
|
418 |
|
|
|
419 |
# simply verifies some attributes of the computer
|
|
|
420 |
sub updateComputerMakeup {
|
|
|
421 |
my ($systemHash) = @_;
|
|
|
422 |
#print "[$$systemHash{'memory'}]\n";
|
|
|
423 |
&checkAndUpdateAttribute($computerID,'Memory',$$systemHash{'memory'});
|
|
|
424 |
#print "[$$systemHash{'num_cpu'}]\n";
|
|
|
425 |
&checkAndUpdateAttribute($computerID,'Number of CPUs',$$systemHash{'num_cpu'});
|
|
|
426 |
#die;
|
|
|
427 |
&checkAndUpdateAttribute($computerID,'CPU Type',$$systemHash{'cpu_type'});
|
|
|
428 |
&checkAndUpdateAttribute($computerID,'CPU SubType',$$systemHash{'cpu_sub'});
|
|
|
429 |
&checkAndUpdateAttribute($computerID,'CPU Speed',$$systemHash{'cpu_speed'});
|
|
|
430 |
}
|
|
|
431 |
|
|
|
432 |
sub updateOS {
|
|
|
433 |
my ($osHash) = @_;
|
|
|
434 |
# verify the operating system
|
|
|
435 |
my $os_id = &getOSID($osHash, $reportDate);
|
|
|
436 |
$sql = qq/select operating_system_id from device_operating_system where device_id = $computerID and removed_date is null/;
|
|
|
437 |
$registeredOS = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
438 |
unless ($registeredOS && $registeredOS eq $os_id ) {
|
|
|
439 |
if ( $registeredOS ) { #we have the same computer, but a new OS???
|
|
|
440 |
$sql = qq/update device_operating_system set removed_date = $reportDate where device_id = $computerID and removed_date is null/;
|
|
|
441 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
442 |
push @warnings, &createLogMessage("Computer $computerName has a new OS" );
|
|
|
443 |
$os_id = &getOSID($osHash, $reportDate);
|
|
|
444 |
}
|
|
|
445 |
$sql = qq/insert into device_operating_system( device_id,operating_system_id,added_date) values ($computerID,$os_id,$reportDate)/;
|
|
|
446 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
447 |
}
|
|
|
448 |
}
|
|
|
449 |
|
|
|
450 |
sub dateToMySQL {
|
|
|
451 |
my $date = shift;
|
|
|
452 |
# print "Date In $date\n";
|
|
|
453 |
$date =~ s/'//g; # some of the older reports put quotes around this
|
|
|
454 |
return &fixDatabaseValue($date) if $date =~ m/\d{4}[-\/]\d{2}[-\/]\d{2} \d{2}:\d{2}/; # this is already in the correct format
|
|
|
455 |
|
|
|
456 |
my ($ss,$mm,$hh,$day,$month,$year,$zone);
|
|
|
457 |
unless ( $date =~ m/^\d+$/ ) { # If it is not a unix time stamp
|
|
|
458 |
$date = str2time($date); # try to parse it
|
|
|
459 |
}
|
|
|
460 |
return '' unless defined $date && $date; # bail if date is not defined or zero
|
|
|
461 |
# standard processing of a date
|
|
|
462 |
($ss,$mm,$hh,$day,$month,$year,$zone) = localtime($date);
|
|
|
463 |
$year += 1900;
|
|
|
464 |
++$month;
|
|
|
465 |
# printf( "Answer Is: %4d-%02d-%02d %02d:%02d\n", $year,$month,$day,$hh,$mm);
|
|
|
466 |
return &fixDatabaseValue(sprintf( '%4d-%02d-%02d %02d:%02d', $year,$month,$day,$hh,$mm));
|
|
|
467 |
}
|
|
|
468 |
|
|
|
469 |
# every time we get a report, we need to see if the computer was rebooted
|
|
|
470 |
# if last reboot date is not the same as what our report shows, we will
|
|
|
471 |
# remove the existing entry, then add a new one
|
|
|
472 |
sub updateBootTime {
|
|
|
473 |
my ($systemHash) = @_;
|
|
|
474 |
my $lastReboot;
|
|
|
475 |
if ($$systemHash{'last_boot'}) {
|
|
|
476 |
#print "Checking Boot Time\n";
|
|
|
477 |
if ($lastReboot = &dateToMySQL($$systemHash{'last_boot'})) {
|
|
|
478 |
my $sql = qq/select computer_uptime_id from computer_uptime where device_id = $computerID and last_reboot = $lastReboot/;
|
|
|
479 |
unless ( &GenericSQL::getOneValue( $dbh, $sql ) ) {
|
|
|
480 |
push @warnings, &createLogMessage("Computer was rebooted at $lastReboot");
|
|
|
481 |
my $sql_insert = qq/update computer_uptime set removed_date = $reportDate where device_id = $computerID and removed_date is null/;
|
|
|
482 |
&GenericSQL::doSQL( $dbh, $sql_insert );
|
|
|
483 |
$sql_insert = qq/insert into computer_uptime (device_id,added_date,last_reboot) values ($computerID,$reportDate,$lastReboot)/;
|
|
|
484 |
&GenericSQL::doSQL( $dbh, $sql_insert );
|
|
|
485 |
}
|
|
|
486 |
} else {
|
|
|
487 |
push @warnings, &createLogMessage('Invalid reboot time [' . $$systemHash{'last_boot'} . ']');
|
|
|
488 |
}
|
|
|
489 |
} else {
|
|
|
490 |
push @warnings, &createLogMessage('No Boot time given');
|
|
|
491 |
}
|
|
|
492 |
}
|
|
|
493 |
|
|
|
494 |
# routine will check for all IP addresses reported and check against those recorded in the
|
|
|
495 |
# database. It will remove any no longer in the database, and add any new ones
|
|
|
496 |
sub doIPAddresses {
|
|
|
497 |
my ( $networkHash ) = @_;
|
|
|
498 |
# delete $$networkHash{'lo'}; # we don't process lo
|
|
|
499 |
# first, remove any interfaces that no longer exist
|
|
|
500 |
my $interfaces = join ',', (map { &fixDatabaseValue($_) } keys %$networkHash); # get a list of interfaces being passed in
|
|
|
501 |
if ( $interfaces ) {
|
|
|
502 |
my $sql = qq/update network set removed_date = $reportDate where device_id = $computerID and removed_date is null and interface not in ($interfaces)/;
|
|
|
503 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
504 |
}
|
|
|
505 |
# let's get all remaining network information
|
|
|
506 |
$sql = qq/select network_id,interface,address,netmask,ip6,ip6net,mac,mtu from network where device_id = $computerID and removed_date is null/;
|
|
|
507 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
508 |
while (my $thisRow = &GenericSQL::getNextRow($sth)) {
|
|
|
509 |
if ( defined $$thisRow{'interface'} ) { # pre 2.0 versions did not have an interface object
|
|
|
510 |
# long drawn out thing to check if they are the same
|
|
|
511 |
if ( &checkEquals($$networkHash{$$thisRow{'interface'}}{'address'}, $$thisRow{'address'}) &&
|
|
|
512 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'ip6address'}, $$thisRow{'ip6'}) &&
|
|
|
513 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'ip6networkbits'}, $$thisRow{'ip6net'}) &&
|
|
|
514 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'mac'}, $$thisRow{'mac'}) &&
|
|
|
515 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'mtu'}, $$thisRow{'mtu'}) &&
|
|
|
516 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'netmask'}, $$thisRow{'netmask'}) ) {
|
|
|
517 |
# they are the same, so just mark it off the list
|
|
|
518 |
delete $$networkHash{$$thisRow{'interface'}};
|
|
|
519 |
} else { # it has changed, so invalidate the current line in the database
|
|
|
520 |
$sql = qq/update network set removed_date = $reportDate where network_id = $$thisRow{'network_id'}/;
|
|
|
521 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
522 |
}
|
|
|
523 |
} else { # the database is still using pre 2.0 values, so we must see if we need to upgrade this
|
|
|
524 |
if ($datafileVersion =~ m/^[23]/) { # version 2.x, or 3.x so we will need to update this record
|
|
|
525 |
# in this case, we are going to just "remove" all current entries and reload them below.
|
|
|
526 |
# this code will only be run once for each machine that needs to conver to the new format
|
|
|
527 |
$sql = qq/update network set removed_date = $reportDate where removed_date is null and device_id = $computerID/;
|
|
|
528 |
last;
|
|
|
529 |
}
|
|
|
530 |
}
|
|
|
531 |
}
|
|
|
532 |
# at this point, the only items left are either new or have changed, so just insert them.
|
|
|
533 |
foreach my $device ( keys %$networkHash ) {
|
|
|
534 |
$sql = qq/insert into network (device_id,added_date,interface,address,netmask,ip6,ip6net,mtu,mac) values /;
|
|
|
535 |
$sql .= '( ' . join(',',
|
|
|
536 |
$computerID,
|
|
|
537 |
$reportDate,
|
|
|
538 |
&fixDatabaseValue($device),
|
|
|
539 |
&fixDatabaseValue($$networkHash{$device}{'address'}),
|
|
|
540 |
&fixDatabaseValue($$networkHash{$device}{'netmask'}),
|
|
|
541 |
&fixDatabaseValue($$networkHash{$device}{'ip6address'}),
|
|
|
542 |
&fixDatabaseValue($$networkHash{$device}{'ip6networkbits'}),
|
|
|
543 |
&fixDatabaseValue($$networkHash{$device}{'mtu'}),
|
|
|
544 |
&fixDatabaseValue($$networkHash{$device}{'mac'})
|
|
|
545 |
) .
|
|
|
546 |
')';
|
|
|
547 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
548 |
push @warnings,&createLogMessage("Network Device $device was added/modified");
|
|
|
549 |
}
|
|
|
550 |
} # sub doIPAddresses
|
|
|
551 |
|
|
|
552 |
|
|
|
553 |
sub processDisks {
|
|
|
554 |
my ($diskHash) = @_;
|
|
|
555 |
#print Data::Dumper->Dump([$diskHash],['$diskHash']);
|
|
|
556 |
#print "Upon entry, we have " . (scalar keys %$diskHash) . " Items in hash\n";
|
|
|
557 |
# first, see if there are any alerts
|
|
|
558 |
foreach my $partition (keys %$diskHash) {
|
|
|
559 |
if ($$diskHash{$partition}{'size'}) {
|
|
|
560 |
my $usedPercent = sprintf('%4.2f', ($$diskHash{$partition}{'used'}/$$diskHash{$partition}{'size'}) * 100);
|
|
|
561 |
push @warnings, &createLogMessage("Partition $partition at $usedPercent%% capacity") if $usedPercent > $DiskUsageAlert;
|
|
|
562 |
}
|
|
|
563 |
}
|
|
|
564 |
# now, remove any that are no longer reported
|
|
|
565 |
my $temp = join ',', (map { &fixDatabaseValue($_) } keys %$diskHash); # get a list of interfaces being passed in
|
|
|
566 |
my $sql = qq/select disk_info_id from disk_info where removed_date is null and device_id = $computerID and disk_device not in ($temp)/;
|
|
|
567 |
#print "\n$sql\n";
|
|
|
568 |
# die;
|
|
|
569 |
my @idsToDelete = &GenericSQL::getArrayOfValues( $dbh, $sql );
|
|
|
570 |
# print '[' . join ('][', @idsToDelete) . "]\n";
|
|
|
571 |
foreach my $id ( @idsToDelete ) {
|
|
|
572 |
next unless $id;
|
|
|
573 |
push @warnings,&createLogMessage("Disk Partition removed");
|
|
|
574 |
$sql = qq/update disk_info set removed_date = $reportDate where removed_date is null and disk_info_id = $id/;
|
|
|
575 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
576 |
$sql = qq/update disk_space set removed_date = $reportDate where removed_date is null and disk_info_id = $id/;
|
|
|
577 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
578 |
}
|
|
|
579 |
# now, we have a "clean" database
|
|
|
580 |
# do a query to retrieve all disk entries for this device
|
|
|
581 |
$sql = qq/select disk_info.disk_info_id,disk_space_id,disk_device,filesystem,mount_point,capacity
|
|
|
582 |
from disk_info join disk_space using (disk_info_id)
|
|
|
583 |
where disk_space.removed_date is null and disk_info.removed_date is null and device_id = $computerID/;
|
|
|
584 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
585 |
#print "Before we start processing, we have " . (scalar keys %$diskHash) . " Items in hash\n";
|
|
|
586 |
while (my $thisDBRow = &GenericSQL::getNextRow($sth)) {
|
|
|
587 |
my $thisHashRow = $$diskHash{$$thisDBRow{'disk_device'}} ; # just for convenience
|
|
|
588 |
# Always invalidate the disk space entry. We'll either add a new row, or it is changed too much
|
|
|
589 |
$sql = "update disk_space set removed_date = $reportDate where removed_date is null and disk_info_id = " . $$thisDBRow{'disk_info_id'};
|
|
|
590 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
591 |
# we know this exists in both
|
|
|
592 |
#print "\n\n" . $$thisDBRow{'disk_device'} . "\n";
|
|
|
593 |
#print Data::Dumper->Dump([$thisDBRow],['thisRow']);
|
|
|
594 |
#print Data::Dumper->Dump([$thisHashRow],['thisHashRow']);
|
|
|
595 |
#print $$thisHashRow{'fstype'} . "\n";
|
|
|
596 |
#print $$thisHashRow{'size'} . "\n";
|
|
|
597 |
#print $$thisHashRow{'mount'} . "\n";
|
|
|
598 |
|
|
|
599 |
# is it a partition, or a directory to watch. This is defined as a directory to watch does not contain a size,
|
|
|
600 |
# mount point or file system type.
|
|
|
601 |
my $diskPartition = (exists ($$thisHashRow{'fstype'}) && exists ($$thisHashRow{'size'}) && exists ($$thisHashRow{'mount'}) );
|
|
|
602 |
# now, determine if we need to update the disk_info for some reason
|
|
|
603 |
# this condition is based upon two types of entries
|
|
|
604 |
# Type #1 (top) is a standard partition, so we see if fstype, mount and capacity are the same
|
|
|
605 |
# type #2 (after ||) us a "directory to watch" (with no fstype, size or mount)
|
|
|
606 |
if ( $diskPartition ) { # it is a partition, so check if something has changed in the entry
|
|
|
607 |
#print "\n\nDevice: [" . $$thisDBRow{'disk_device'} . "] is a partition\n";
|
|
|
608 |
#print "thisHashRow fstype [" . $$thisHashRow{'fstype'} . "]\n";
|
|
|
609 |
#print "thisHashRow size [" . $$thisHashRow{'size'} . "]\n";
|
|
|
610 |
#print "thisHashRow mount [" . $$thisHashRow{'mount'} . "]\n";
|
|
|
611 |
#print "thisRow filesystem [" .
|
|
|
612 |
unless ( &checkEquals($$thisHashRow{'fstype'}, $$thisDBRow{'filesystem'}) and
|
|
|
613 |
&checkEquals($$thisHashRow{'mount'}, $$thisDBRow{'mount_point'}) and
|
|
|
614 |
&checkEquals($$thisHashRow{'size'}, $$thisDBRow{'capacity'}) ) {
|
|
|
615 |
# yes, a change. If we just remove this entry, the add loop (below) will set it as a new device
|
|
|
616 |
$sql = "update disk_info set removed_date = $reportDate where disk_info_id = " . $$thisDBRow{'disk_info_id'};
|
|
|
617 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
618 |
#print "$sql\n";
|
|
|
619 |
next;
|
|
|
620 |
}
|
|
|
621 |
}
|
|
|
622 |
$usedSpace = $$diskHash{$$thisDBRow{'disk_device'}}{'used'};
|
|
|
623 |
#print "\tupdating entry, looking at disk has => $$thisDBRow{'disk_device'} with space $usedSpace\n";
|
|
|
624 |
$sql = "insert into disk_space (disk_info_id,space_used,added_date) values ";
|
|
|
625 |
$sql .= '(' . join (',', ($$thisDBRow{'disk_info_id'}, &fixDatabaseValue($usedSpace), $reportDate)) . ')';
|
|
|
626 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
627 |
# and delete the hash entry so we don't process it as a change
|
|
|
628 |
delete $$diskHash{$$thisDBRow{'disk_device'}};
|
|
|
629 |
}
|
|
|
630 |
# at this point, all we have left are additions and changes
|
|
|
631 |
foreach my $partition ( keys %$diskHash ) {
|
|
|
632 |
$sql = 'insert into disk_info(device_id,added_date,disk_device,filesystem,mount_point,capacity) values ';
|
|
|
633 |
$sql .= '(' . join( ',', ( $computerID,
|
|
|
634 |
$reportDate,
|
|
|
635 |
&fixDatabaseValue($partition),
|
|
|
636 |
&fixDatabaseValue($$diskHash{$partition}{'fstype'}),
|
|
|
637 |
&fixDatabaseValue($$diskHash{$partition}{'mount'}),
|
|
|
638 |
&fixDatabaseValue($$diskHash{$partition}{'size'})
|
|
|
639 |
)
|
|
|
640 |
) . ')';
|
|
|
641 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
642 |
$sql = "select disk_info_id from disk_info where removed_date is null and device_id = $computerID and disk_device = " . &fixDatabaseValue($partition);
|
|
|
643 |
$temp = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
644 |
$sql = 'insert into disk_space(disk_info_id,added_date,space_used) values (';
|
|
|
645 |
$sql .= join( ',', ($temp, $reportDate, fixDatabaseValue($$diskHash{$partition}{'used'}))) . ')';
|
|
|
646 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
647 |
}
|
|
|
648 |
}
|
|
|
649 |
|
|
|
650 |
# routine to ensure the hardware returned as PCI hardware is in the attributes area
|
|
|
651 |
sub processPCI {
|
|
|
652 |
my ($pciHash) = @_;
|
|
|
653 |
# print "Entering processPCI\n";
|
|
|
654 |
#print Data::Dumper->Dump([$pciHash],[$key]);
|
|
|
655 |
return unless $pciHash && keys %$pciHash;
|
|
|
656 |
|
|
|
657 |
#my %attributeMappings = ('class' => 'Class', # v2 database has these items, but we want to have a pretty name
|
|
|
658 |
# 'device' => 'Device Name',
|
|
|
659 |
# 'sdevice' => 'Subsystem Device',
|
|
|
660 |
# 'svendor' => 'Subsystem Vendor',
|
|
|
661 |
# 'vendor' => 'Vendor',
|
|
|
662 |
# 'name' => 'Name',
|
|
|
663 |
# 'slot' => 'Slot'
|
|
|
664 |
# );
|
|
|
665 |
|
|
|
666 |
# The two keys we'll check for uniquness are device.name and device_type with a key value of 'slot'. If these
|
|
|
667 |
# are the same, we assume this is the same record
|
|
|
668 |
|
|
|
669 |
# print Data::Dumper->Dump([$pciHash]);
|
|
|
670 |
|
|
|
671 |
my $key;
|
|
|
672 |
# normalize the data
|
|
|
673 |
foreach $key ( keys %$pciHash ) {
|
|
|
674 |
unless ( defined ($$pciHash{$key}{'slot'}) ) { # doesn't have a slot field
|
|
|
675 |
my $slotField = '';
|
|
|
676 |
my $test = $$pciHash{$key};
|
|
|
677 |
foreach $subkey ( keys %$test) { # scan through all keys and see if there is something with a "slot looking" value in it
|
|
|
678 |
$slotField = $key if $$test{$subkey} =~ m/^[0-9a-f:.]+$/;
|
|
|
679 |
}
|
|
|
680 |
if ( $slotField ) {
|
|
|
681 |
$$pciHash{$key}{$subkey}{'slot'} = $$pciHash{$key}{$subkey}{$slotField};
|
|
|
682 |
} else {
|
|
|
683 |
$$pciHash{$key}{'slot'} = 'Unknown';
|
|
|
684 |
}
|
|
|
685 |
}
|
|
|
686 |
# Each entry must have a name. Use 'device' if it doesn't exist
|
|
|
687 |
$$pciHash{$key}{'name'} = $$pciHash{$key}{'device'} unless defined($$pciHash{$key}{'name'}) && $$pciHash{$key}{'name'};
|
|
|
688 |
$$pciHash{$key}{'name'} = $$pciHash{$key}{'sdevice'} unless defined($$pciHash{$key}{'name'}) && $$pciHash{$key}{'name'};
|
|
|
689 |
$$pciHash{$key}{'name'} =~ s/^ +//;
|
|
|
690 |
unless ( $$pciHash{$key}{'name'} ) {
|
|
|
691 |
push @warnings, &createLogMessage("No name given for one or more PCI devices at normalize, Computer ID: [$computerID], Report Date: [$reportDate]");
|
|
|
692 |
return;
|
|
|
693 |
}
|
|
|
694 |
# Following is what will actually be put in the device table, ie device.name
|
|
|
695 |
$$pciHash{$key}{'keyFieldValue'} = $$pciHash{$key}{'slot'} . ' - ' . $$pciHash{$key}{'name'};
|
|
|
696 |
}
|
|
|
697 |
# at this point, we should have a slot and a name field in all pci devices
|
|
|
698 |
|
|
|
699 |
# print Data::Dumper->Dump([$pciHash]);
|
|
|
700 |
# die;
|
|
|
701 |
# Get list of all PCI cards in database for this computer
|
|
|
702 |
my @toDelete;
|
|
|
703 |
$sql = qq/select device_id,
|
|
|
704 |
device.name name
|
|
|
705 |
from device join device_type using (device_type_id)
|
|
|
706 |
where device_type.name = 'PCI Card'
|
|
|
707 |
and device.removed_date is null
|
|
|
708 |
and device.part_of = $computerID/;
|
|
|
709 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
710 |
while (my $thisRow = &GenericSQL::getNextRow($sth)) { # for each row in the database
|
|
|
711 |
my $deleteMe = $$thisRow{'device_id'}; # assume we will delete it
|
|
|
712 |
foreach $key (keys %$pciHash ) { # look for it in the hash
|
|
|
713 |
#print "Checking [$$pciHash{$key}{'name'}] eq [$$thisRow{'name'}]\n";
|
|
|
714 |
#print " [$$pciHash{$key}{'slot'}] eq [$$thisRow{'slot'}]\n\n";
|
|
|
715 |
if (
|
|
|
716 |
($$pciHash{$key}{'keyFieldValue'} eq $$thisRow{'name'})
|
|
|
717 |
&&
|
|
|
718 |
! defined ($$pciHash{$key}{'device_id'}) # this keeps us from ignoring a card when two are installed
|
|
|
719 |
) { # it is in the database and in pciHash
|
|
|
720 |
$deleteMe = ''; # so let's keep it
|
|
|
721 |
$$pciHash{$key}{'device_id'} = $$thisRow{'device_id'}; # and mark it as there
|
|
|
722 |
#print "\tfound equality at $$thisRow{'device_id'}\n";
|
|
|
723 |
last; # and exit the foreach loop
|
|
|
724 |
}
|
|
|
725 |
}
|
|
|
726 |
push @toDelete, $deleteMe if $deleteMe; # if we did not find it, mark for deletion
|
|
|
727 |
}
|
|
|
728 |
# remove stale items from the database
|
|
|
729 |
if (@toDelete) {
|
|
|
730 |
my $toDelete = join ",", @toDelete; # this is a list of device_id's
|
|
|
731 |
push @warnings, &createLogMessage( scalar(@toDelete) . " PCI Devices removed");
|
|
|
732 |
# remove from the device_attrib table
|
|
|
733 |
$sql = qq/update device_attrib set removed_date = $reportDate where device_id in ($toDelete)/;
|
|
|
734 |
# print "$sql\n";
|
|
|
735 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
736 |
# and from the device table itself
|
|
|
737 |
$sql = qq/update device set removed_date = $reportDate where device_id in ($toDelete)/;
|
|
|
738 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
739 |
}
|
|
|
740 |
undef @toDelete; # don't need this anymore
|
|
|
741 |
|
|
|
742 |
my $added = 0;
|
|
|
743 |
my $updated = 0;
|
|
|
744 |
# now, we have either inserts or updates
|
|
|
745 |
foreach $key (keys %$pciHash) {
|
|
|
746 |
unless ( $$pciHash{$key}{'device_id'} ) { # we did not find it in the database, so it is an insert
|
|
|
747 |
my $thisKey = &fixDatabaseValue($$pciHash{$key}{'keyFieldValue'});
|
|
|
748 |
$sql = qq/insert into device (site_id,device_type_id,name,part_of,added_date)
|
|
|
749 |
select site_id,device_type.device_type_id, $thisKey, device_id, $reportDate
|
|
|
750 |
from device,device_type
|
|
|
751 |
where device.device_id = $computerID
|
|
|
752 |
and device_type.name = 'PCI Card'/;
|
|
|
753 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
754 |
# get the inserted key
|
|
|
755 |
$$pciHash{$key}{'device_id'} = &GenericSQL::getOneValue($dbh, qq/select max(device_id) from device where part_of = $computerID and name = $thisKey and added_date = $reportDate/);
|
|
|
756 |
$added++;
|
|
|
757 |
} # unless
|
|
|
758 |
my $thisEntry = $$pciHash{$key};
|
|
|
759 |
$value = 0;
|
|
|
760 |
foreach my $subkey ( keys %$thisEntry ) {
|
|
|
761 |
# $test = $attributeMappings{$subkey} ? $attributeMappings{$subkey} : $subkey;
|
|
|
762 |
# print "checking $subkey [$$thisEntry{$subkey}]\n";
|
|
|
763 |
$value += &checkAndUpdateAttribute($$pciHash{$key}{'device_id'},
|
|
|
764 |
$attributeMappings{$subkey} ? $attributeMappings{$subkey} : $subkey,
|
|
|
765 |
$$thisEntry{$subkey} )
|
|
|
766 |
unless ($subkey eq 'device_id') or ($subkey eq 'keyFieldValue');
|
|
|
767 |
}
|
|
|
768 |
$updated++ if $value;
|
|
|
769 |
}
|
|
|
770 |
push @warnings, &createLogMessage("$added PCI Devices added") if $added;
|
|
|
771 |
push @warnings, &createLogMessage("$updated PCI Devices modified") if $updated;
|
|
|
772 |
}
|
|
|
773 |
|
|
|
774 |
|
|
|
775 |
# figure out the software_id and software_version_id of a package. Adds the package/version if
|
|
|
776 |
# it doesn't exist in the database
|
|
|
777 |
sub getSoftwareID {
|
|
|
778 |
my ( $packageName,$versionInfo,$description ) = @_;
|
|
|
779 |
#print "In getSoftwareID, paramters are [$packageName][$versionInfo][$description]\n";
|
|
|
780 |
#return;
|
|
|
781 |
# escape and quote the values for SQL
|
|
|
782 |
$packageName = &GenericSQL::fixStringValue($dbh, $packageName );
|
|
|
783 |
$versionInfo = &GenericSQL::fixStringValue($dbh, $versionInfo );
|
|
|
784 |
# does the package exist?
|
|
|
785 |
my $sql = qq/select software_id from software where package_name = $packageName and removed_date is null/;
|
|
|
786 |
my $result = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
787 |
unless ( $result ) { # NO, package doesn't exist, so add it to the database
|
|
|
788 |
$description = &GenericSQL::fixStringValue($dbh, $description );
|
|
|
789 |
$sql = qq/insert into software (package_name,description, added_date) values ($packageName,$description, $reportDate)/;
|
|
|
790 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
791 |
$sql = qq/select software_id from software where package_name = $packageName and removed_date is null/;
|
|
|
792 |
$result = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
793 |
}
|
|
|
794 |
# does this version number exist?
|
|
|
795 |
$sql = qq/select software_version_id from software_version where version = $versionInfo and removed_date is null/;
|
|
|
796 |
my $version = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
797 |
unless ( $version ) { # nope, so add it
|
|
|
798 |
$sql = qq/insert into software_version ( version,added_date ) values ($versionInfo,$reportDate)/;
|
|
|
799 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
800 |
$sql = qq/select software_version_id from software_version where version = $versionInfo and removed_date is null/;
|
|
|
801 |
$version = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
802 |
}
|
|
|
803 |
return ($result,$version);
|
|
|
804 |
}
|
|
|
805 |
|
|
|
806 |
# process each package. We will only add entries if a package has changed, either version number
|
|
|
807 |
# added, or deleted. Deleted packages are not handled well right now.
|
|
|
808 |
sub processPackages {
|
|
|
809 |
my ($softwareHash) = @_;
|
|
|
810 |
my %softwareIDs;
|
|
|
811 |
my $count;
|
|
|
812 |
# since we go by software and version id's, let's just precalculate them
|
|
|
813 |
foreach my $package (keys %$softwareHash) {
|
|
|
814 |
# this will also insert the package and/or version in the software or software_version tables
|
|
|
815 |
($$softwareHash{$package}{'softwareid'},$$softwareHash{$package}{'versionid'}) =
|
|
|
816 |
&getSoftwareID( $package, $$softwareHash{$package}{'version'}, $$softwareHash{$package}{'description'}, $reportDate );
|
|
|
817 |
# this is just a shortcut for when we need to query
|
|
|
818 |
#$$softwareHash{$package}{'complexkey'} = $$softwareHash{$package}{'softwareid'} . '-' . $$softwareHash{$package}{'versionid'};
|
|
|
819 |
#push @installedPackages,$$softwareHash{$package}{'softwareid'};
|
|
|
820 |
$softwareIDs{$$softwareHash{$package}{'softwareid'}} = $$softwareHash{$package}{'versionid'};
|
|
|
821 |
}
|
|
|
822 |
# remove any software for this machine that no longer exists
|
|
|
823 |
my $temp = join( ',', grep { /^\d+$/ } keys %softwareIDs); # make sure we only have numerics
|
|
|
824 |
my $sql = "update installed_packages set removed_date = $reportDate where device_id = $computerID and removed_date is null and software_id not in ($temp)";
|
|
|
825 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
826 |
# ok, at this point, all software in the database exists in the computer
|
|
|
827 |
# now, lets see if there are any modified versions or something
|
|
|
828 |
$sql = qq/select installed_packages_id,software_id,software_version_id from installed_packages where device_id = $computerID and removed_date is null/;
|
|
|
829 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
830 |
#print "Before we start processing, we have " . (scalar keys %$diskHash) . " Items in hash\n";
|
|
|
831 |
while (my $thisRow = &GenericSQL::getNextRow($sth)) {
|
|
|
832 |
# if the version is the same, just do the next one
|
|
|
833 |
if ( $softwareIDs{$$thisRow{'software_id'}} == $$thisRow{'software_version_id'}) {
|
|
|
834 |
delete $softwareIDs{$$thisRow{'software_id'}};
|
|
|
835 |
} else { # we have a change. We simply remove the entry and let the "add new packages" section take care of it
|
|
|
836 |
$sql = qq/update installed_packages set removed_date = $reportDate where installed_packages_id = $$thisRow{'installed_packages_id'}/;
|
|
|
837 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
838 |
}
|
|
|
839 |
}
|
|
|
840 |
# at this point, the only items left in $softwareIDs are the packages that have changed or been added
|
|
|
841 |
$count = 0;
|
|
|
842 |
foreach my $softwareID ( keys %softwareIDs ) {
|
|
|
843 |
$count++;
|
|
|
844 |
$sql = qq/insert into installed_packages( device_id,software_id,software_version_id,added_date ) values
|
|
|
845 |
($computerID,$softwareID,$softwareIDs{$softwareID},$reportDate)/;
|
|
|
846 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
847 |
}
|
|
|
848 |
push @warnings, &createLogMessage("$count Software Packages changed or added") if $count;
|
|
|
849 |
}
|
|
|
850 |
|
|
|
851 |
###############################################################################
|
|
|
852 |
# BEGIN MAIN ROUTINE
|
|
|
853 |
###############################################################################
|
|
|
854 |
BEGIN{
|
|
|
855 |
# load the configuration file
|
|
|
856 |
eval ( &loadConfigurationFile );
|
|
|
857 |
push @INC, $LIBRARIES;
|
|
|
858 |
}
|
|
|
859 |
|
|
|
860 |
use strict;
|
|
|
861 |
no strict 'vars';
|
|
|
862 |
#use Data::Dumper;
|
|
|
863 |
use GenericSQL; # generic, home grown MySQL access routines
|
|
|
864 |
#use GenericTemplates;
|
|
|
865 |
#use Logging; # generic, home grown logging routines
|
|
|
866 |
use Date::Format; # allows us to format our dates nicely
|
|
|
867 |
use Date::Parse; # VERY KEWL, parses out a huge number of date formats
|
|
|
868 |
|
|
|
869 |
$dbh = DBI->connect( $DSN, $DB_USER , $DB_PASS ) or die $DBI::errstr; # try to connect to db first
|
|
|
870 |
|
|
|
871 |
# read the input, parse it into useable information
|
|
|
872 |
my $data = &readAndParseInput;
|
|
|
873 |
#print Data::Dumper->Dump([$data]);
|
|
|
874 |
#die;
|
|
|
875 |
$reportDate = &dateToMySQL($$data{'report'}{'date'});
|
|
|
876 |
$clientName = $$data{'report'}{'client'};
|
|
|
877 |
$FATAL = 'No client name' unless $clientName;
|
|
|
878 |
$computerName = $$data{'system'}{'hostname'} unless $FATAL;
|
|
|
879 |
$FATAL = 'No computer name' unless $computerName;
|
|
|
880 |
# print STDERR "[$computerName]\n";
|
|
|
881 |
|
|
|
882 |
|
|
|
883 |
# try to figure out who the client is, creating if necessary
|
|
|
884 |
$clientID = &getClientID( ) unless $FATAL;
|
|
|
885 |
# try to figure out the computer ID, creating an entry if necessary
|
|
|
886 |
$computerID = &getComputerID( ) unless $FATAL;
|
|
|
887 |
# Ok, we have enough info, now let's make sure we aren't re-runing a report and record the current one.
|
|
|
888 |
my $reportID = &recordReport( ) unless $FATAL;
|
|
|
889 |
# we will simply verify memory, cpu, etc...
|
|
|
890 |
&updateComputerMakeup($$data{'system'}) unless $FATAL;
|
|
|
891 |
# check if the operating system has changed
|
|
|
892 |
&updateOS( $$data{'operatingsystem'} ) unless $FATAL;
|
|
|
893 |
# see if the machine has been rebooted and, if so, record it
|
|
|
894 |
&updateBootTime ($$data{'system'}) unless $FATAL;
|
|
|
895 |
# see what IP's this machine has
|
|
|
896 |
&doIPAddresses($$data{'network'}) unless $FATAL;
|
|
|
897 |
# Look at the disk usage, and report if they are above limits
|
|
|
898 |
&processDisks($$data{'diskinfo'}) unless $FATAL;
|
|
|
899 |
# and also if any hardware has changed
|
|
|
900 |
&processPCI($$data{'pci'}) unless $FATAL;
|
|
|
901 |
# see if any software packages have changed
|
|
|
902 |
&processPackages($$data{'software'}) unless $FATAL;
|
|
|
903 |
if ($FATAL) { # we had a fatal error, so just return it
|
|
|
904 |
print "ERROR: $FATAL\n";
|
|
|
905 |
exit 0;
|
|
|
906 |
}
|
|
|
907 |
# ok, work is done. If there are any values in $warnings, they should be either printed or e-mailed
|
|
|
908 |
# to whoever the sysadmin is.
|
|
|
909 |
if (@warnings) {
|
|
|
910 |
my $warnings = join ("\n", @warnings);
|
|
|
911 |
if ($iMailResults) {
|
|
|
912 |
&sendmail( $mailFrom, $mailTo, 'Process Sysinfo Warnings', $warnings, $mailCC, $mailBCC, $mailServer, $mailServerPort );
|
|
|
913 |
} else {
|
|
|
914 |
print "$warnings\n";
|
|
|
915 |
}
|
|
|
916 |
}
|
|
|
917 |
|
|
|
918 |
exit 1;
|