+use DateUtils;
+use Display;
+use GetConfig;
+
+our %MACHINESOPTS = GetConfig ("$FindBin::Bin/../etc/machines.conf");
+
+my $defaultFilesystemThreshold = 90;
+my $defaultFilesystemHist = '6 months';
+my $defaultLoadavgHist = '6 months';
+
+# Internal methods
+sub _dberror ($$) {
+ my ($self, $msg, $statement) = @_;
+
+ my $dberr = $self->{db}->err;
+ my $dberrmsg = $self->{db}->errstr;
+
+ $dberr ||= 0;
+ $dberrmsg ||= 'Success';
+
+ my $message = '';
+
+ if ($dberr) {
+ my $function = (caller (1)) [3];
+
+ $message = "$function: $msg\nError #$dberr: $dberrmsg\n"
+ . "SQL Statement: $statement";
+ } # if
+
+ return $dberr, $message;
+} # _dberror
+
+sub _formatValues (@) {
+ my ($self, @values) = @_;
+
+ my @returnValues;
+
+ # Quote data values
+ push @returnValues, $_ eq '' ? 'null' : $self->{db}->quote ($_)
+ foreach (@values);
+
+ return @returnValues;
+} # _formatValues
+
+sub _formatNameValues (%) {
+ my ($self, %rec) = @_;
+
+ my @nameValueStrs;
+
+ push @nameValueStrs, "$_=" . $self->{db}->quote ($rec{$_})
+ foreach (keys %rec);
+
+ return @nameValueStrs;
+} # _formatNameValues
+
+sub _error () {\r
+ my ($self) = @_;
+
+ if ($self->{msg}) {
+ if ($self->{errno}) {
+ carp $self->{msg};
+ } else {
+ cluck $self->{msg};
+ } # if
+ } # if\r
+} # _error
+
+sub _addRecord ($%) {
+ my ($self, $table, %rec) = @_;
+
+ my $statement = "insert into $table (";
+ $statement .= join ',', keys %rec;
+ $statement .= ') values (';
+ $statement .= join ',', $self->_formatValues (values %rec);
+ $statement .= ')';
+
+ my ($err, $msg);
+
+ $self->{db}->do ($statement);
+
+ return $self->_dberror ("Unable to add record to $table", $statement);
+} # _addRecord
+
+sub _checkRequiredFields ($$) {
+ my ($fields, $rec) = @_;
+
+ foreach my $fieldname (@$fields) {
+ my $found = 0;
+
+ foreach (keys %$rec) {
+ if ($fieldname eq $_) {
+ $found = 1;
+ last;
+ } # if
+ } # foreach
+
+ return "$fieldname is required"
+ unless $found;
+ } # foreach
+
+ return;
+} # _checkRequiredFields
+
+sub _connect (;$) {
+ my ($self, $dbserver) = @_;
+
+ $dbserver ||= $MACHINESOPTS{MACHINES_SERVER};
+
+ my $dbname = 'machines';
+ my $dbdriver = 'mysql';
+
+ $self->{db} = DBI->connect (
+ "DBI:$dbdriver:$dbname:$dbserver",
+ $MACHINESOPTS{MACHINES_USERNAME},
+ $MACHINESOPTS{MACHINES_PASSWORD},
+ {PrintError => 0},
+ ) or croak (
+ "Couldn't connect to $dbname database "
+ . "as $MACHINESOPTS{MACHINESADM_USERNAME}\@$MACHINESOPTS{MACHINESADM_SERVER}"
+ );
+
+ $self->{dbserver} = $dbserver;
+
+ return;
+} # _connect
+
+sub _getRecords ($$) {
+ my ($self, $table, $condition) = @_;
+
+ my ($err, $msg);
+
+ my $statement = "select * from $table where $condition";
+
+ my $sth = $self->{db}->prepare ($statement);
+
+ unless ($sth) {
+ ($err, $msg) = $self->_dberror ('Unable to prepare statement', $statement);
+
+ croak $msg;
+ } # if
+
+ my $attempts = 0;
+ my $maxAttempts = 3;
+ my $sleepTime = 30;
+ my $status;
+
+ # We've been having the server going away. Supposedly it should reconnect so
+ # here we simply retry up to $maxAttempts times to re-execute the statement.
+ # (Are there other places where we need to do this?)
+ $err = 2006;
+
+ while ($err == 2006 and $attempts++ < $maxAttempts) {
+ $status = $sth->execute;
+
+ if ($status) {
+ $err = 0;
+ last;
+ } else {
+ ($err, $msg) = $self->_dberror ('Unable to execute statement',
+ $statement);
+ } # if
+
+ last if $err == 0;
+
+ croak $msg unless $err == 2006;
+
+ my $timestamp = YMDHMS;
+
+ $self->Error ("$timestamp: Unable to talk to DB server.\n\n$msg\n\n"
+ . "Will try again in $sleepTime seconds", -1);
+
+ # Try to reconnect
+ $self->_connect ($self->{dbserver});
+
+ sleep $sleepTime;
+ } # while
+
+ $self->Error ("After $maxAttempts attempts I could not connect to the database", $err)
+ if ($err == 2006 and $attempts > $maxAttempts);
+
+ my @records;
+
+ while (my $row = $sth->fetchrow_hashref) {
+ push @records, $row;
+ } # while
+
+ return @records;
+} # _getRecord