From ed7943b5913aae90452e00009a19aaa86605b820 Mon Sep 17 00:00:00 2001 From: Andrew DeFaria Date: Wed, 18 Jul 2018 13:08:18 -0700 Subject: [PATCH] Some cosmetic edits Adding new Machines.pm interface --- clearadm/lib/Clearadm.pm | 904 ++++++++++++++++++------------------ clearadm/lib/ClearadmWeb.pm | 740 ++++++++++++++--------------- clearadm/lib/Clearexec.pm | 8 +- clearadm/lib/User.pm | 42 +- data/machines | 32 +- etc/machines.sql | 62 +++ lib/Clearquest/DBService.pm | 202 ++++---- lib/Clearquest/REST.pm | 321 +++++++------ lib/Clearquest/Server.pm | 194 ++++---- lib/Machines.pm | 357 ++++++++++++++ lib/Machines/MySQL.pm | 242 ++++++++++ rc/gitconfig | 2 + rc/system | 2 +- 13 files changed, 1885 insertions(+), 1223 deletions(-) create mode 100644 etc/machines.sql create mode 100755 lib/Machines.pm create mode 100755 lib/Machines/MySQL.pm diff --git a/clearadm/lib/Clearadm.pm b/clearadm/lib/Clearadm.pm index fb63079..719b772 100644 --- a/clearadm/lib/Clearadm.pm +++ b/clearadm/lib/Clearadm.pm @@ -36,7 +36,7 @@ specifics about the method you are envoking. # Create new Clearadm object my $clearadm = new Clearadm; - + # Add a new system my %system = ( name => 'jupiter', @@ -46,22 +46,22 @@ specifics about the method you are envoking. type => 'Linux', description => 'Home server', ); - + my ($err, $msg) = $clearadm->AddSystem (%system); - + # Find systems matching 'jup' my @systems = $clearadm->FindSystem ('jup'); - + # Get a system by name my %system = $clearadm->GetSystem ('jupiter'); - + # Update system my %update = ( 'region' => 'East Coast', ); my ($err, $msg) = $clearadm->UpdateSystem ('jupiter', %update); - + # Delete system (Warning: will delete all related records regarding this # system). my ($err, $msg) = $clearadm->DeleteSystem ('jupiter'); @@ -69,8 +69,8 @@ specifics about the method you are envoking. =head1 DESCRIPTION This package provides and object oriented interface to the Clearadm database. -Methods are provided to manipulate records by adding, updating and deleting -them. In general you need to specify a hash which contains keys and values +Methods are provided to manipulate records by adding, updating and deleting +them. In general you need to specify a hash which contains keys and values corresponding to the database field names and values. =head1 ROUTINES @@ -105,19 +105,19 @@ our %CLEAROPTS = GetConfig ($conf); # Globals our $VERSION = '$Revision: 1.54 $'; ($VERSION) = ($VERSION =~ /\$Revision: (.*) /); - -$CLEAROPTS{CLEARADM_USERNAME} = $ENV{CLEARADM_USERNAME} + +$CLEAROPTS{CLEARADM_USERNAME} = $ENV{CLEARADM_USERNAME} ? $ENV{CLEARADM_USERNAME} : $CLEAROPTS{CLEARADM_USERNAME} ? $CLEAROPTS{CLEARADM_USERNAME} : 'clearwriter'; -$CLEAROPTS{CLEARADM_PASSWORD} = $ENV{CLEARADM_PASSWORD} +$CLEAROPTS{CLEARADM_PASSWORD} = $ENV{CLEARADM_PASSWORD} ? $ENV{CLEARADM_PASSWORD} : $CLEAROPTS{CLEARADM_PASSWORD} ? $CLEAROPTS{CLEARADM_PASSWORD} : 'clearwriter'; -$CLEAROPTS{CLEARADM_SERVER} = $ENV{CLEARADM_SERVER} - ? $ENV{CLEARADM_SERVER} +$CLEAROPTS{CLEARADM_SERVER} = $ENV{CLEARADM_SERVER} + ? $ENV{CLEARADM_SERVER} : $CLEAROPTS{CLEARADM_SERVER} ? $CLEAROPTS{CLEARADM_SERVER} : 'localhost'; @@ -132,12 +132,12 @@ sub _dberror ($$) { my $dberr = $self->{db}->err; my $dberrmsg = $self->{db}->errstr; - + $dberr ||= 0; $dberrmsg ||= 'Success'; my $message = ''; - + if ($dberr) { my $function = (caller (1)) [3]; @@ -145,82 +145,82 @@ sub _dberror ($$) { . "SQL Statement: $statement"; } # if - return $dberr, $message; + return $dberr, $message; } # _dberror sub _formatValues (@) { my ($self, @values) = @_; - + my @returnValues; - + # Quote data values - push @returnValues, $_ eq '' ? 'null' : $self->{db}->quote ($_) - foreach (@values); - + push @returnValues, $_ eq '' ? 'null' : $self->{db}->quote ($_) + for (@values); + return @returnValues; } # _formatValues sub _formatNameValues (%) { my ($self, %rec) = @_; - + my @nameValueStrs; - + push @nameValueStrs, "$_=" . $self->{db}->quote ($rec{$_}) - foreach (keys %rec); - + for (keys %rec); + return @nameValueStrs; } # _formatNameValues 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 _deleteRecord ($;$) { my ($self, $table, $condition) = @_; - + my $count; - + my $statement = "select count(*) from $table "; $statement .= "where $condition" if $condition; - + my $sth = $self->{db}->prepare ($statement) or return $self->_dberror ('Unable to prepare statement', $statement); - + $sth->execute or return $self->_dberror ('Unable to execute statement', $statement); - + my @row = $sth->fetchrow_array; - + $sth->finish; - + if ($row[0]) { $count = $row[0]; } else { $count = 0; } # if - + return ($count, 'Records deleted') if $count == 0; - + $statement = "delete from $table "; $statement .= "where $condition" if $condition; - + $self->{db}->do ($statement); - + if ($self->{db}->err) { return $self->_dberror ("Unable to delete record from $table", $statement); } else { @@ -235,44 +235,44 @@ sub _updateRecord ($$%) { $statement .= join ',', $self->_formatNameValues (%rec); $statement .= " where $condition" if $condition; - + $self->{db}->do ($statement); - + return $self->_dberror ("Unable to update record in $table", $statement); } # _updateRecord sub _checkRequiredFields ($$) { my ($fields, $rec) = @_; - - foreach my $fieldname (@$fields) { + + for my $fieldname (@$fields) { my $found = 0; - - foreach (keys %$rec) { + + for (keys %$rec) { if ($fieldname eq $_) { $found = 1; last; } # if - } # foreach - + } # for + return "$fieldname is required" unless $found; - } # foreach - + } # for + return; } # _checkRequiredFields 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 @@ -280,15 +280,15 @@ sub _getRecords ($$) { 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. + # 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; @@ -296,16 +296,16 @@ sub _getRecords ($$) { ($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}); @@ -314,21 +314,21 @@ sub _getRecords ($$) { $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 sub _aliasSystem ($) { my ($self, $system) = @_; - + my %system = $self->GetSystem ($system); - + if ($system{name}) { return $system{name}; } else { @@ -338,54 +338,54 @@ sub _aliasSystem ($) { sub _getLastID () { my ($self) = @_; - + my $statement = 'select last_insert_id()'; - + my $sth = $self->{db}->prepare ($statement); - + my ($err, $msg); - + unless ($sth) { ($err, $msg) = $self->_dberror ('Unable to prepare statement', $statement); - + croak $msg; } # if - + my $status = $sth->execute; - + unless ($status) { ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement); - + croak $msg; } # if - + my @records; my @row = $sth->fetchrow_array; - + return $row[0]; } # _getLastID sub _connect (;$) { my ($self, $dbserver) = @_; - + $dbserver ||= $CLEAROPTS{CLEARADM_SERVER}; - + my $dbname = 'clearadm'; my $dbdriver = 'mysql'; $self->{db} = DBI->connect ( - "DBI:$dbdriver:$dbname:$dbserver", + "DBI:$dbdriver:$dbname:$dbserver", $CLEAROPTS{CLEARADM_USERNAME}, $CLEAROPTS{CLEARADM_PASSWORD}, {PrintError => 0}, ) or croak ( - "Couldn't connect to $dbname database " + "Couldn't connect to $dbname database " . "as $CLEAROPTS{CLEARADM_USERNAME}\@$CLEAROPTS{CLEARADM_SERVER}" ); - + $self->{dbserver} = $dbserver; - + return; } # _connect @@ -401,9 +401,9 @@ sub new (;$) { sub SetNotify () { my ($self) = @_; - + $self->{NOTIFY} = $CLEAROPTS{CLEARADM_NOTIFY}; - + return; } # SetNotify @@ -411,9 +411,9 @@ sub Error ($;$) { my ($self, $msg, $errno) = @_; # If $errno is specified we need to stop. However we need to notify somebody - # that cleartasks is no longer running. + # that cleartasks is no longer running. error $msg; - + if ($errno) { if ($self->{NOTIFY}) { mail ( @@ -422,35 +422,35 @@ sub Error ($;$) { data => "

An unexpected, internal error occurred in Clearadm:

$msg

", mode => 'html', ); - + exit $errno if $errno > 0; } # if } # if - + return; } # Error sub AddSystem (%) { my ($self, %system) = @_; - + my @requiredFields = ( 'name', ); my $result = _checkRequiredFields \@requiredFields, \%system; - + return -1, "AddSystem: $result" if $result; - + $system{loadavgHist} ||= $defaultLoadavgHist; - + return $self->_addRecord ('system', %system); } # AddSystem sub DeleteSystem ($) { my ($self, $name) = @_; - return $self->_deleteRecord ('system', "name='$name'"); + return $self->_deleteRecord ('system', "name='$name'"); } # DeleteSystem sub UpdateSystem ($%) { @@ -461,15 +461,15 @@ sub UpdateSystem ($%) { sub GetSystem ($) { my ($self, $system) = @_; - + return unless $system; - + my @records = $self->_getRecords ( - 'system', + 'system', "name='$system' or alias like '%$system%'" ); - + if ($records[0]) { return %{$records[0]}; } else { @@ -481,23 +481,23 @@ sub FindSystem (;$) { my ($self, $system) = @_; $system ||= ''; - + my $condition = "name like '%$system%' or alias like '%$system%'"; - + return $self->_getRecords ('system', $condition); } # FindSystem sub SearchSystem (;$) { my ($self, $condition) = @_; - + $condition = "name like '%'" unless $condition; - - return $self->_getRecords ('system', $condition); + + return $self->_getRecords ('system', $condition); } # SearchSystem sub AddPackage (%) { my ($self, %package) = @_; - + my @requiredFields = ( 'system', 'name', @@ -505,48 +505,48 @@ sub AddPackage (%) { ); my $result = _checkRequiredFields \@requiredFields, \%package; - + return -1, "AddPackage: $result" if $result; - + return $self->_addRecord ('package', %package); } # AddPackage sub DeletePackage ($$) { my ($self, $system, $name) = @_; - + return $self->_deleteRecord ( - 'package', + 'package', "(system='$system' or alias='$system') and name='$name'"); } # DeletePackage sub UpdatePackage ($$%) { my ($self, $system, $name, %update) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + return $self->_updateRecord ('package', "system='$system'", %update); } # UpdatePackage sub GetPackage($$) { my ($self, $system, $name) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + return unless $name; - + my @records = $self->_getRecords ( - 'package', + 'package', "system='$system' and name='$name'" ); - + if ($records[0]) { return %{$records[0]}; } else { @@ -560,18 +560,18 @@ sub FindPackage ($;$) { $name ||= ''; $system = $self->_aliasSystem ($system); - + return unless $system; - + my $condition = "system='$system' and name like '%$name%'"; - + return $self->_getRecords ('package', $condition); } # FindPackage sub AddFilesystem (%) { my ($self, %filesystem) = @_; - + my @requiredFields = ( 'system', 'filesystem', @@ -579,38 +579,38 @@ sub AddFilesystem (%) { ); my $result = _checkRequiredFields \@requiredFields, \%filesystem; - + return -1, "AddFilesystem: $result" if $result; - + # Default filesystem threshold $filesystem{threshold} ||= $defaultFilesystemThreshold; - + return $self->_addRecord ('filesystem', %filesystem); } # AddFilesystem sub DeleteFilesystem ($$) { my ($self, $system, $filesystem) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + return $self->_deleteRecord ( - 'filesystem', + 'filesystem', "system='$system' and filesystem='$filesystem'" ); } # DeleteFilesystem sub UpdateFilesystem ($$%) { my ($self, $system, $filesystem, %update) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + return $self->_updateRecord ( 'filesystem', "system='$system' and filesystem='$filesystem'", @@ -620,20 +620,20 @@ sub UpdateFilesystem ($$%) { sub GetFilesystem ($$) { my ($self, $system, $filesystem) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + return unless $filesystem; - + my @records = $self->_getRecords ( - 'filesystem', + 'filesystem', "system='$system' and filesystem='$filesystem'" ); - + if ($records[0]) { return %{$records[0]}; } else { @@ -643,49 +643,49 @@ sub GetFilesystem ($$) { sub FindFilesystem ($;$) { my ($self, $system, $filesystem) = @_; - + $filesystem ||= ''; $system = $self->_aliasSystem ($system); - + return unless $system; - my $condition = "system='$system' and filesystem like '%$filesystem%'"; - + my $condition = "system='$system' and filesystem like '%$filesystem%'"; + return $self->_getRecords ('filesystem', $condition); } # FindFilesystem sub AddVob (%) { my ($self, %vob) = @_; - + my @requiredFields = ( 'system', 'tag', ); my $result = _checkRequiredFields \@requiredFields, \%vob; - + return -1, "AddVob: $result" if $result; - + return $self->_addRecord ('vob', %vob); } # AddVob sub DeleteVob ($) { my ($self, $tag) = @_; - + return $self->_deleteRecord ('vob', "tag='$tag'"); } # DeleteVob sub GetVob ($) { my ($self, $tag) = @_; - - return + + return unless $tag; - + my @records = $self->_getRecords ('vob', "tag='$tag'"); - + if ($records[0]) { return %{$records[0]}; } else { @@ -695,40 +695,40 @@ sub GetVob ($) { sub FindVob ($) { my ($self, $tag) = @_; - + return $self->_getRecords ('vob', "tag like '%$tag%'"); } # FindVob sub AddView (%) { my ($self, %view) = @_; - + my @requiredFields = ( 'system', 'tag', ); my $result = _checkRequiredFields \@requiredFields, \%view; - + return -1, "AddView: $result" if $result; - + return $self->_addRecord ('view', %view); } # AddView sub DeleteView ($) { my ($self, $tag) = @_; - + return $self->_deleteRecord ('vob', "tag='$tag'"); } # DeleteView sub GetView ($) { my ($self, $tag) = @_; - + return unless $tag; - + my @records = $self->_getRecords ('view', "tag='$tag'"); - + if ($records[0]) { return %{$records[0]}; } else { @@ -743,9 +743,9 @@ sub FindView (;$$$$) { $region ||= ''; $tag ||= ''; $ownerName ||= ''; - + my $condition; - + $condition = "system like '%$system%'"; $condition .= ' and '; $condition = "region like '%$region%'"; @@ -753,44 +753,44 @@ sub FindView (;$$$$) { $condition .= "tag like '%$tag'"; $condition .= ' and '; $condition .= "ownerName like '%$ownerName'"; - + return $self->_getRecords ('view', $condition); } # FindView sub AddFS (%) { my ($self, %fs) = @_; - + my @requiredFields = ( 'system', 'filesystem', ); my $result = _checkRequiredFields \@requiredFields, \%fs; - + return -1, "AddFS: $result" if $result; - + # Timestamp record $fs{timestamp} = Today2SQLDatetime; - + return $self->_addRecord ('fs', %fs); } # AddFS sub TrimFS ($$) { my ($self, $system, $filesystem) = @_; - + my %filesystem = $self->GetFilesystem ($system, $filesystem); - + return unless %filesystem; - + my %task = $self->GetTask ('scrub'); - + $self->Error ("Unable to find scrub task!", 1) unless %task; - + my $days; my $today = Today2SQLDatetime; - + # TODO: SubtractDays uses just an approximation (i.e. subtracting 30 days when # in February is not right. if ($filesystem{filesystemHist} =~ /(\d+) month/i) { @@ -800,44 +800,44 @@ sub TrimFS ($$) { } # if my $oldage = SubtractDays $today, $days; - + my ($dberr, $dbmsg) = $self->_deleteRecord ( 'fs', "system='$system' and filesystem='$filesystem' and timestamp<='$oldage'" ); - + if ($dbmsg eq 'Records deleted') { return (0, $dbmsg) if $dberr == 0; - + my %runlog; - + $runlog{task} = $task{name}; $runlog{started} = $today; $runlog{status} = 0; - $runlog{message} = + $runlog{message} = "Scrubbed $dberr fs records for filesystem $system:$filesystem"; - + my ($err, $msg) = $self->AddRunlog (%runlog); - + $self->Error ("Unable to add runlog - (Error: $err)\n$msg") if $err; } # if - + return ($dberr, $dbmsg); } # TrimFS sub TrimLoadavg ($) { my ($self, $system) = @_; - + my %system = $self->GetSystem ($system); - + return unless %system; - + my %task = $self->GetTask ('loadavg'); - + $self->Error ("Unable to find loadavg task!", 1) unless %task; - + my $days; my $today = Today2SQLDatetime; @@ -850,26 +850,26 @@ sub TrimLoadavg ($) { } # if my $oldage = SubtractDays $today, $days; - + my ($dberr, $dbmsg) = $self->_deleteRecord ( 'loadavg', "system='$system' and timestamp<='$oldage'" ); - + if ($dbmsg eq 'Records deleted') { return (0, $dbmsg) if $dberr == 0; - + my %runlog; - + $runlog{task} = $task{name}; $runlog{started} = $today; $runlog{status} = 0; - $runlog{message} = + $runlog{message} = "Scrubbed $dberr loadavg records for system $system"; my ($err, $msg) = $self->AddRunlog (%runlog); - + $self->Error ("Unable to add runload (Error: $err)\n$msg") if $err; } # if @@ -878,17 +878,17 @@ sub TrimLoadavg ($) { sub GetFS ($$;$$$$) { my ($self, $system, $filesystem, $start, $end, $count, $interval) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + return unless $filesystem; - + $interval ||= 'Minute'; - + my $size = $interval =~ /month/i ? 7 : $interval =~ /day/i @@ -896,16 +896,16 @@ sub GetFS ($$;$$$$) { : $interval =~ /hour/i ? 13 : 16; - + undef $start if $start and $start =~ /earliest/i; undef $end if $end and $end =~ /latest/i; - + my $condition = "system='$system' and filesystem='$filesystem'"; $condition .= " and timestamp>='$start'" if $start; $condition .= " and timestamp<='$end'" if $end; - + $condition .= " group by left(timestamp,$size)"; - + if ($count) { # We can't simply do a "limit 0, $count" as that just gets the front end of # the records return (i.e. if $count = say 10 and the timestamp range @@ -914,14 +914,14 @@ sub GetFS ($$;$$$$) { # $count my $nbrRecs = $self->Count ('fs', $condition); my $offset = $nbrRecs - $count; - + # Offsets of < 0 are not allowed. $offset = 0 if $offset < 0; $condition .= " limit $offset, $count"; } # if - + my $statement = <<"END"; select system, @@ -938,49 +938,49 @@ from END my ($err, $msg); - + my $sth = $self->{db}->prepare ($statement); - + unless ($sth) { ($err, $msg) = $self->_dberror ('Unable to prepare statement', $statement); - + croak $msg; } # if - + my $status = $sth->execute; - + unless ($status) { ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement); - + croak $msg; } # if - + my @records; - + while (my $row = $sth->fetchrow_hashref) { push @records, $row; } # while - + return @records; } # GetFS sub GetLatestFS ($$) { my ($self, $system, $filesystem) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + return unless $filesystem; - + my @records = $self->_getRecords ( 'fs', "system='$system' and filesystem='$filesystem'" . " order by timestamp desc limit 0, 1", ); - + if ($records[0]) { return %{$records[0]}; } else { @@ -990,32 +990,32 @@ sub GetLatestFS ($$) { sub AddLoadavg () { my ($self, %loadavg) = @_; - + my @requiredFields = ( 'system', ); my $result = _checkRequiredFields \@requiredFields, \%loadavg; - + return -1, "AddLoadavg: $result" if $result; - + # Timestamp record $loadavg{timestamp} = Today2SQLDatetime; - + return $self->_addRecord ('loadavg', %loadavg); } # AddLoadavg sub GetLoadavg ($;$$$$) { my ($self, $system, $start, $end, $count, $interval) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + $interval ||= 'Minute'; - + my $size = $interval =~ /month/i ? 7 : $interval =~ /day/i @@ -1023,16 +1023,16 @@ sub GetLoadavg ($;$$$$) { : $interval =~ /hour/i ? 13 : 16; - + my $condition; - + undef $start if $start and $start =~ /earliest/i; undef $end if $end and $end =~ /latest/i; - + $condition .= " system='$system'" if $system; $condition .= " and timestamp>='$start'" if $start; $condition .= " and timestamp<='$end'" if $end; - + $condition .= " group by left(timestamp,$size)"; if ($count) { @@ -1043,14 +1043,14 @@ sub GetLoadavg ($;$$$$) { # $count my $nbrRecs = $self->Count ('loadavg', $condition); my $offset = $nbrRecs - $count; - + # Offsets of < 0 are not allowed. $offset = 0 if $offset < 0; $condition .= " limit $offset, $count"; } # if - + my $statement = <<"END"; select system, @@ -1064,46 +1064,46 @@ from END my ($err, $msg); - + my $sth = $self->{db}->prepare ($statement); - + unless ($sth) { ($err, $msg) = $self->_dberror ('Unable to prepare statement', $statement); - + croak $msg; } # if - + my $status = $sth->execute; - + unless ($status) { ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement); - + croak $msg; } # if - + my @records; - + while (my $row = $sth->fetchrow_hashref) { push @records, $row; } # while - + return @records; } # GetLoadvg sub GetLatestLoadavg ($) { my ($self, $system) = @_; - + $system = $self->_aliasSystem ($system); - + return unless $system; - + my @records = $self->_getRecords ( 'loadavg', "system='$system'" . " order by timestamp desc limit 0, 1", ); - + if ($records[0]) { return %{$records[0]}; } else { @@ -1113,123 +1113,123 @@ sub GetLatestLoadavg ($) { sub AddTask (%) { my ($self, %task) = @_; - + my @requiredFields = ( 'name', 'command' ); - + my $result = _checkRequiredFields \@requiredFields, \%task; - + return -1, "AddTask: $result" if $result; - - return $self->_addRecord ('task', %task); + + return $self->_addRecord ('task', %task); } # AddTask sub DeleteTask ($) { my ($self, $name) = @_; - + return $self->_deleteRecord ('task', "name='$name'"); } # DeleteTask sub FindTask ($) { my ($self, $name) = @_; - + $name ||= ''; - + my $condition = "name like '%$name%'"; - + return $self->_getRecords ('task', $condition); } # FindTask sub GetTask ($) { my ($self, $name) = @_; - + return unless $name; - + my @records = $self->_getRecords ('task', "name='$name'"); if ($records[0]) { return %{$records[0]}; } else { return; - } # if + } # if } # GetTask sub UpdateTask ($%) { my ($self, $name, %update) = @_; - + return $self->_updateRecord ('task', "name='$name'", %update); } # Update sub AddSchedule (%) { my ($self, %schedule) = @_; - + my @requiredFields = ( 'task', ); - + my $result = _checkRequiredFields \@requiredFields, \%schedule; - + return -1, "AddSchedule: $result" if $result; - - return $self->_addRecord ('schedule', %schedule); + + return $self->_addRecord ('schedule', %schedule); } # AddSchedule sub DeleteSchedule ($) { my ($self, $name) = @_; - + return $self->_deleteRecord ('schedule', "name='$name'"); } # DeleteSchedule sub FindSchedule (;$$) { my ($self, $name, $task) = @_; - + $name ||= ''; $task||= ''; - + my $condition = "name like '%$name%'"; $condition .= ' and '; $condition .= "task like '%$task%'"; - return $self->_getRecords ('schedule', $condition); + return $self->_getRecords ('schedule', $condition); } # FindSchedule sub GetSchedule ($) { my ($self, $name) = @_; - + my @records = $self->_getRecords ('schedule', "name='$name'"); - + if ($records[0]) { return %{$records[0]}; } else { return; - } # if + } # if } # GetSchedule sub UpdateSchedule ($%) { my ($self, $name, %update) = @_; - + return $self->_updateRecord ('schedule', "name='$name'", %update); } # UpdateSchedule sub AddRunlog (%) { my ($self, %runlog) = @_; - + my @requiredFields = ( 'task', ); - + my $result = _checkRequiredFields \@requiredFields, \%runlog; - + return -1, "AddRunlog: $result" if $result; - + $runlog{ended} = Today2SQLDatetime; - + my ($err, $msg) = $self->_addRecord ('runlog', %runlog); return ($err, $msg, $self->_getLastID); @@ -1237,29 +1237,29 @@ sub AddRunlog (%) { sub DeleteRunlog ($) { my ($self, $condition) = @_; - + return $self->_deleteRecord ('runlog', $condition); } # DeleteRunlog sub FindRunlog (;$$$$$$) { my ($self, $task, $system, $status, $id, $start, $page) = @_; - + $task ||= ''; - + # If ID is specified then that's all that really matters as it uniquely # identifies a runlog entry; my $condition; - + unless ($id) { $condition = "task like '%$task%'"; - + if ($system) { $condition .= " and system like '%$system%'" unless $system eq 'All'; } else { $condition .= ' and system is null'; } # unless - + if (defined $status) { if ($status =~ /!(-*\d+)/) { $condition .= " and status<>$1"; @@ -1267,9 +1267,9 @@ sub FindRunlog (;$$$$$$) { $condition .= " and status=$status" } # if } # if - - $condition .= " order by started desc"; - + + $condition .= " order by started desc"; + if (defined $start) { $page ||= 10; $condition .= " limit $start, $page"; @@ -1277,60 +1277,60 @@ sub FindRunlog (;$$$$$$) { } else { $condition = "id=$id"; } # unless - + return $self->_getRecords ('runlog', $condition); } # FindRunlog sub GetRunlog ($) { my ($self, $id) = @_; - + return unless $id; - + my @records = $self->_getRecords ('runlog', "id=$id"); - + if ($records[0]) { return %{$records[0]}; } else { return; - } # if + } # if } # GetRunlog sub UpdateRunlog ($%) { my ($self, $id, %update) = @_; - + return $self->_updateRecord ('runlog', "id=$id", %update); } # UpdateRunlog sub Count ($;$) { my ($self, $table, $condition) = @_; - + $condition = $condition ? 'where ' . $condition : ''; - + my ($err, $msg); - + my $statement = "select count(*) from $table $condition"; - + my $sth = $self->{db}->prepare ($statement); - + unless ($sth) { ($err, $msg) = $self->_dberror ('Unable to prepare statement', $statement); - + croak $msg; } # if - + my $status = $sth->execute; - + unless ($status) { ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement); - + croak $msg; } # if - + # Hack! Statements such as the following: # # select count(*) from fs where system='jupiter' and filesystem='/dev/sdb5' - # > group by left(timestamp,10); + # > group by left(timestamp,10); # +----------+ # | count(*) | # +----------+ @@ -1343,7 +1343,7 @@ sub Count ($;$) { # | 190 | # +----------+ # 7 rows in set (0.00 sec) - # + # # Here we want 7 but what we see in $records[0] is 49. So the hack is that if # statement contains "group by" then we assume we have the above and return # scalar @records, otherwise we return $records[0]; @@ -1365,9 +1365,9 @@ sub Count ($;$) { # us again. sub GetWork () { my ($self) = @_; - + my ($err, $msg); - + my $statement = <<"END"; select schedule.name as schedulename, @@ -1385,43 +1385,43 @@ where and schedule.active='true' order by lastrun END - + my $sth = $self->{db}->prepare ($statement); - + unless ($sth) { ($err, $msg) = $self->_dberror ('Unable to prepare statement', $statement); - + croak $msg; } # if - + my $status = $sth->execute; - + unless ($status) { ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement); - + croak $msg; } # if - - my $sleep; + + my $sleep; my @records; - + while (my $row = $sth->fetchrow_hashref) { if ($$row{system} !~ /localhost/i) { my %system = $self->GetSystem ($$row{system}); - + # Skip inactive systems next if $system{active} eq 'false'; } # if - + # If started is not defined then this task was never run so run it now. unless ($$row{lastrun}) { push @records, $row; next; } # unless - + # TODO: Handle frequencies better. my $seconds; - + if ($$row{frequency} =~ /(\d+) seconds/i) { $seconds = $1; } elsif ($$row{frequency} =~ /(\d+) minute/i) { @@ -1434,23 +1434,23 @@ END warning "Don't know how to handle frequencies like $$row{frequency}"; next; } # if - + my $today = Today2SQLDatetime; my $lastrun = Add ($$row{lastrun}, (seconds => $seconds)); my $waitTime = DateToEpoch ($lastrun) - DateToEpoch ($today); - + if ($waitTime < 0) { # We're late - push this onto records and move on push @records, $row; } # if - + $sleep ||= $waitTime; - + if ($sleep > $waitTime) { $sleep = $waitTime; } # if } # while - + # Even if there is nothing to do the caller should sleep a bit and come back # to us. So if it ends up there's nothing past due, and nothing upcoming, then # sleep for a minute and return here. Somebody may have added a new task next @@ -1458,91 +1458,91 @@ END if (@records == 0 and not $sleep) { $sleep = 60; } # if - - return ($sleep, @records); + + return ($sleep, @records); } # GetWork sub GetUniqueList ($$) { my ($self, $table, $field) = @_; - + my ($err, $msg); - + my $statement = "select $field from $table group by $field"; - + my $sth = $self->{db}->prepare ($statement); - + unless ($sth) { ($err, $msg) = $self->_dberror ('Unable to prepare statement', $statement); - + croak $msg; } # if - + my $status = $sth->execute; - + unless ($status) { ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement); - + croak $msg; } # if my @values; - + while (my @row = $sth->fetchrow_array) { if ($row[0]) { push @values, $row[0]; } else { push @values, ''; } # if - } # foreach + } # for return @values; } # GetUniqueList sub AddAlert(%) { my ($self, %alert) = @_; - + my @requiredFields = ( 'name', 'type', ); - + my $result = _checkRequiredFields \@requiredFields, \%alert; - + return -1, "AddAlert: $result" if $result; - - return $self->_addRecord ('alert', %alert); + + return $self->_addRecord ('alert', %alert); } # AddAlert sub DeleteAlert ($) { my ($self, $name) = @_; - + return $self->_deleteRecord ('alert', "name='$name'"); } # DeleteAlert sub FindAlert (;$) { my ($self, $alert) = @_; - + $alert ||= ''; - + my $condition = "name like '%$alert%'"; - - return $self->_getRecords ('alert', $condition); + + return $self->_getRecords ('alert', $condition); } # FindAlert sub GetAlert ($) { my ($self, $name) = @_; - + return unless $name; - + my @records = $self->_getRecords ('alert', "name='$name'"); - + if ($records[0]) { return %{$records[0]}; } else { return; - } # if + } # if } # GetAlert sub SendAlert ($$$$$$$) { @@ -1556,25 +1556,25 @@ sub SendAlert ($$$$$$$) { $to, $runlogID, ) = @_; - + my $footing = '

'; $footing .= ''; my $year = (localtime)[5] + 1900; - $footing .= "Clearadm
"; + $footing .= "Clearadm
"; $footing .= "Copyright © $year, ClearSCM, Inc. - All rights reserved"; - + my %alert = $self->GetAlert ($alert); - + if ($alert{type} eq 'email') { my $from = 'Clearadm@' . hostdomain; - + mail ( from => $from, to => $to, subject => "Clearadm Alert: $system: $subject", mode => 'html', - data => $message, - footing => $footing, + data => $message, + footing => $footing, ); } else { $self->Error ("Don't know how to send $alert{type} alerts\n" @@ -1589,40 +1589,40 @@ sub SendAlert ($$$$$$$) { notification => $notification, runlog => $runlogID, timestamp => Today2SQLDatetime, - message => $subject, - ); - + message => $subject, + ); + return $self->AddAlertlog (%alertlog); } # SendAlert sub GetLastAlert ($$) { my ($self, $notification, $system) = @_; - + my $statement = <<"END"; select runlog, timestamp -from +from alertlog where notification='$notification' and system='$system' order by timestamp desc -limit +limit 0, 1 END - + my $sth = $self->{db}->prepare ($statement) or return $self->_dberror ('Unable to prepare statement', $statement); - + $sth->execute or return $self->_dberror ('Unable to execute statement', $statement); - + my $alertlog= $sth->fetchrow_hashref; - + $sth->finish; - + if ($alertlog) { return %$alertlog; } else { @@ -1632,38 +1632,38 @@ END sub GetLastTaskFailure ($$) { my ($self, $task, $system) = @_; - + my $statement = <<"END"; select id, ended -from +from runlog where - status <> 0 + status <> 0 and task='$task' and system='$system' and alerted='true' order by ended desc -limit +limit 0, 1 END - + my $sth = $self->{db}->prepare ($statement) or return $self->_dberror ('Unable to prepare statement', $statement); - + $sth->execute or return $self->_dberror ('Unable to execute statement', $statement); - + my $runlog= $sth->fetchrow_hashref; - + $sth->finish; - + if ($$runlog{ended}) { return %$runlog; } # if - + # If we didn't get any ended in the last call then there's nothing that # qualified. Still let's return a record (%runlog) that has a valid id so # that the caller can update that runlog with alerted = 'true'. @@ -1676,7 +1676,7 @@ where status <> 0 and task='$task' and system='$system' -order by +order by ended desc limit 0, 1 @@ -1684,20 +1684,20 @@ END $sth = $self->{db}->prepare ($statement) or return $self->_dberror ('Unable to prepare statement', $statement); - + $sth->execute or return $self->_dberror ('Unable to execute statement', $statement); - + $runlog = $sth->fetchrow_hashref; - + $sth->finish; - + if ($runlog) { return %$runlog; } else { return } # if -} # GetLastTaskFailure +} # GetLastTaskFailure sub Notify ($$$$$$) { my ( @@ -1713,9 +1713,9 @@ sub Notify ($$$$$$) { $runlogID = $self->_getLastID unless $runlogID; - + my ($err, $msg); - + # Update filesystem, if $filesystem was specified if ($filesystem) { ($err, $msg) = $self->UpdateFilesystem ( @@ -1724,26 +1724,26 @@ sub Notify ($$$$$$) { notification => $notification, ), ); - + $self->Error ("Unable to set notification for filesystem $system:$filesystem " . "(Status: $err)\n$msg", $err) if $err; } # if - + # Update system ($err, $msg) = $self->UpdateSystem ( $system, ( notification => $notification, ), ); - + my %notification = $self->GetNotification ($notification); - + my %lastnotified = $self->GetLastAlert ($notification, $system); - + if (%lastnotified and $lastnotified{timestamp}) { my $today = Today2SQLDatetime; my $lastnotified = $lastnotified{timestamp}; - + if ($notification{nomorethan} =~ /hour/i) { $lastnotified = Add ($lastnotified, (hours => 1)); } elsif ($notification{nomorethan} =~ /day/i) { @@ -1753,13 +1753,13 @@ sub Notify ($$$$$$) { } elsif ($notification{nomorethan} =~ /month/i) { $lastnotified = Add ($lastnotified, (month => 1)); } # if - + # If you want to fake an alert in the debugger just change $diff accordingly my $diff = Compare ($today, $lastnotified); - + return if $diff <= 0; - } # if + } # if my $when = Today2SQLDatetime; my $nomorethan = lc $notification{nomorethan}; @@ -1771,7 +1771,7 @@ sub Notify ($$$$$$) { unless ($to) { if ($system) { my %system = $self->GetSystem ($system); - + $to = $system{email}; } else { # If we don't know what system this error occurred on we'll have to notify @@ -1780,13 +1780,13 @@ sub Notify ($$$$$$) { $to = $self->{NOTIFY}; } # if } # unless - + unless ($to) { Error "To undefined"; } # unless - + $message .= "

You will receive this alert no more than $nomorethan.

"; - + ($err, $msg) = $self->SendAlert ( $notification{alert}, $system, @@ -1796,7 +1796,7 @@ sub Notify ($$$$$$) { $to, $runlogID, ); - + $self->Error ("Unable to send alert (Status: $err)\n$msg", $err) if $err; verbose "Sent alert to $to"; @@ -1807,44 +1807,44 @@ sub Notify ($$$$$$) { alerted => 'true', ), ); - + $self->Error ("Unable to update runlog (Status: $err)\n$msg", $err) if $err; - return; + return; } # Notify sub ClearNotifications ($$;$) { my ($self, $system, $filesystem) = @_; - + my ($err, $msg); - + if ($filesystem) { ($err, $msg) = $self->UpdateFilesystem ( $system, $filesystem, (notification => undef), ); - + error "Unable to clear notification for filesystem $system:$filesystem " . "(Status: $err)\n$msg", $err if $err; - + # Check to see any of this system's filesystems have notifications. If none - # then it's save to say we've turned off the last notification for a + # then it's save to say we've turned off the last notification for a # filesystem involved with this system and if $system{notification} was # 'Filesystem' then we can toggle off the notification on the system too my $filesystemsAlerted = 0; - - foreach ($self->FindFilesystem ($system)) { - $filesystemsAlerted++ + + for ($self->FindFilesystem ($system)) { + $filesystemsAlerted++ if $$_{notification}; - } # foreach - + } # for + my %system = $self->GetSystem ($system); - + return unless $system; - - if ($system{notification} and + + if ($system{notification} and $system{notification} eq 'Filesystem' and $filesystemsAlerted == 0) { ($err, $msg) = $self->UpdateSystem ($system, (notification => undef)); @@ -1854,11 +1854,11 @@ sub ClearNotifications ($$;$) { } # if } else { ($err, $msg) = $self->UpdateSystem ($system, (notification => undef)); - + $self->Error ("Unable to clear notification for system $system " . "(Status: $err)\n$msg", $err) if $err; } # if - + return; } # ClearNotifications @@ -1867,20 +1867,20 @@ sub SystemAlive (%) { # If we've never heard from this system then we will assume that the system # has not been set up to run clearagent and has never checked in. In any event - # we cannot say the system died because we've never known it to be alive! + # we cannot say the system died because we've never known it to be alive! return 1 unless $system{lastheardfrom}; - + # If a system is not active (may have been temporarily been deactivated) then # we don't want to turn on the bells and whistles alerting people it's down. return 1 if $system{active} eq 'false'; - + my $today = Today2SQLDatetime; my $lastheardfrom = $system{lastheardfrom}; - + my $tenMinutes = 10 * 60; - + $lastheardfrom = Add ($lastheardfrom, (seconds => $tenMinutes)); if (DateToEpoch ($lastheardfrom) < DateToEpoch ($today)) { @@ -1889,7 +1889,7 @@ sub SystemAlive (%) { notification => 'Heartbeat' ), ); - + return; } else { if ($system{notification}) { @@ -1905,7 +1905,7 @@ sub SystemAlive (%) { sub UpdateAlert ($%) { my ($self, $name, %update) = @_; - + return $self->_updateRecord ( 'alert', "name='$name'", @@ -1915,29 +1915,29 @@ sub UpdateAlert ($%) { sub AddAlertlog (%) { my ($self, %alertlog) = @_; - + my @requiredFields = ( 'alert', 'notification', ); - + my $result = _checkRequiredFields \@requiredFields, \%alertlog; - + return -1, "AddAlertlog: $result" if $result; - + # Timestamp record $alertlog{timestamp} = Today2SQLDatetime; - - return $self->_addRecord ('alertlog', %alertlog); + + return $self->_addRecord ('alertlog', %alertlog); } # AddAlertlog sub DeleteAlertlog ($) { my ($self, $condition) = @_; - + return unless $condition; - + if ($condition =~ /all/i) { return $self->_deleteRecord ('alertlog'); } else { @@ -1947,18 +1947,18 @@ sub DeleteAlertlog ($) { sub FindAlertlog (;$$$$$) { my ($self, $alert, $system, $notification, $start, $page) = @_; - + $alert ||= ''; $system ||= ''; $notification ||= ''; - + my $condition = "alert like '%$alert%'"; $condition .= ' and '; $condition .= "system like '%$system%'"; $condition .= ' and '; $condition .= "notification like '%$notification%'"; $condition .= " order by timestamp desc"; - + if (defined $start) { $page ||= 10; $condition .= " limit $start, $page"; @@ -1969,22 +1969,22 @@ sub FindAlertlog (;$$$$$) { sub GetAlertlog ($) { my ($self, $alert) = @_; - + return unless $alert; - + my @records = $self->_getRecords ('alertlog', "alert='$alert'"); - + if ($records[0]) { return %{$records[0]}; } else { return; - } # if + } # if } # GetAlertlog sub UpdateAlertlog ($%) { my ($self, $alert, %update) = @_; - + return $self->_updateRecord ( 'alertlog', "alert='$alert'", @@ -1994,57 +1994,57 @@ sub UpdateAlertlog ($%) { sub AddNotification (%) { my ($self, %notification) = @_; - + my @requiredFields = ( 'name', 'alert', 'cond' ); - + my $result = _checkRequiredFields \@requiredFields, \%notification; - + return -1, "AddNotification: $result" if $result; - - return $self->_addRecord ('notification', %notification); + + return $self->_addRecord ('notification', %notification); } # AddNotification sub DeleteNotification ($) { my ($self, $name) = @_; - + return $self->_deleteRecord ('notification', "name='$name'"); } # DeletePackage sub FindNotification (;$$) { my ($self, $name, $cond, $ordering) = @_; - + $name ||= ''; - + my $condition = "name like '%$name%'"; $condition .= " and $cond" if $cond; - - return $self->_getRecords ('notification', $condition); + + return $self->_getRecords ('notification', $condition); } # FindNotification sub GetNotification ($) { my ($self, $name) = @_; - + return unless $name; - + my @records = $self->_getRecords ('notification', "name='$name'"); - + if ($records[0]) { return %{$records[0]}; } else { return; - } # if + } # if } # GetNotification sub UpdateNotification ($%) { my ($self, $name, %update) = @_; - + return $self->_updateRecord ( 'notification', "name='$name'", @@ -2078,7 +2078,7 @@ L =head2 ClearSCM Perl Modules -=begin man +=begin man DateUtils Display diff --git a/clearadm/lib/ClearadmWeb.pm b/clearadm/lib/ClearadmWeb.pm index d449473..17ccb6e 100644 --- a/clearadm/lib/ClearadmWeb.pm +++ b/clearadm/lib/ClearadmWeb.pm @@ -48,7 +48,7 @@ use strict; use base 'Exporter'; use CGI qw ( - :standard + :standard start_a end_a start_div @@ -82,7 +82,7 @@ my $clearadm = Clearadm->new; our $APPNAME= 'Clearadm'; our $VERSION = '$Revision: 1.46 $'; ($VERSION) = ($VERSION =~ /\$Revision: (.*) /); - + our @EXPORT = qw ( autoScale displayError @@ -160,74 +160,74 @@ our @PREDEFINED_MULTIPLIERS = ( sub dbug ($) { my ($msg) = @_; - + display font ({-class => 'error'}, '
DEBUG: '). $msg; - + return; } # dbug sub displayError ($) { my ($msg) = @_; - + display font ({-class => 'error'}, 'Error: ') . $msg; - + return } # displayError; sub setField ($;$) { my ($field, $label) = @_; - + $label ||= 'Unknown'; my $undef = font {-class => 'unknown'}, $label; - + return defined $field ? $field : $undef; } # setField sub setFields ($%) { my ($label, %rec) = @_; - + $rec{$_} = setField ($rec{$_}, $label) foreach keys %rec; - + return %rec; } # setFields; sub dumpVars (%) { my (%vars) = @_; - + foreach (keys %vars) { dbug "$_: $vars{$_}"; } # foreach - + return; } # dumpVars sub graphError ($) { my ($msg) = @_; - + use GD; - + # Make the image fit the message. It seems that characters are ~ 7px wide. my $imageLength = length ($msg) * 7; - + my $errorImage = GD::Image->new ($imageLength, 20); # Allocate some colors my $white = $errorImage->colorAllocate (255, 255, 255); my $red = $errorImage->colorAllocate (255, 0, 0); - + # Allow the text to shine through $errorImage->transparent($white); $errorImage->interlaced('true'); - # Now put out the message + # Now put out the message $errorImage->string (gdMediumBoldFont, 0, 0, $msg, $red); # And return it print "Content-type: image/png\n\n"; print $errorImage->png; - + # Since we've "returned" the error in the form of an image, there's nothing # left for us to do so we can exit exit; @@ -235,29 +235,29 @@ sub graphError ($) { sub autoScale ($) { my ($amount) = @_; - + my $kbyte = 1024; my $meg = (1024 * $kbyte); my $gig = (1024 * $meg); - + my $size = $amount > $gig ? sprintf ('%.2f Gig', $amount / $gig) : $amount > $meg ? sprintf ('%.2f Meg', $amount / $meg) : sprintf ('%.2f Kbyte', $amount / $kbyte); - - return $size; + + return $size; } # autoScale sub _makeAlertlogSelection ($$) { my ($name, $default) = @_; - + $default ||= 'All'; my %values; $values{All} = 'All'; - + $values{$$_{$name}} = $$_{$name} foreach ($clearadm->FindAlertlog); @@ -266,128 +266,128 @@ sub _makeAlertlogSelection ($$) { class => 'dropdown', values => [sort keys %values], default => $default, - }; - + }; + return $dropdown; } # _makeAlertlogSelection sub _makeRunlogSelection ($$) { my ($name, $default) = @_; - + $default ||= 'All'; my @values = sort $clearadm->GetUniqueList ('runlog', $name); - + unshift @values, 'All'; - + my %values; - + foreach (@values) { unless ($_ eq '') { $values{$_} = $_; } else { $values{NULL} = ''; - } #if + } #if } # foreach - + my $dropdown = popup_menu { name => $name, class => 'dropdown', values => \@values, default => $default, labels => \%values, - }; - + }; + return $dropdown; } # _makeRunlogSelection sub _makeRunlogSelectionNumeric ($$) { my ($name, $default) = @_; - + $default ||= 'All'; my @values = sort {$a <=> $b} $clearadm->GetUniqueList ('runlog', $name); - + unshift @values, 'All'; - + my $dropdown = popup_menu { name => $name, class => 'dropdown', values => [@values], default => $default, - }; - + }; + return $dropdown; } # _makeRunlogSelection sub makeAlertDropdown (;$$) { my ($label, $default) = @_; - + $label ||= ''; my @values; push @values, $$_{name} foreach ($clearadm->FindAlert); - + my $dropdown = "$label "; $dropdown .= popup_menu { name => 'alert', class => 'dropdown', values => [sort @values], default => $default, - }; - + }; + return $dropdown; } # makeAlertDropdown sub makeMultiplierDropdown (;$$) { my ($label, $default) = @_; - + $label ||= ''; - + my $dropdown = "$label "; $dropdown .= popup_menu { name => 'multiplier', class => 'dropdown', values => [sort @PREDEFINED_MULTIPLIERS], default => $default, - }; + }; return $dropdown; } # makeMultiplierDropdown sub makeNoMoreThanDropdown (;$$) { my ($label, $default) = @_; - + $label ||= ''; - + my $dropdown = "$label "; $dropdown .= popup_menu { name => 'nomorethan', class => 'dropdown', values => [sort @PREDEFINED_NOTMORETHAN], default => $default, - }; + }; return $dropdown; } # makeNoMorThanDropdown - + sub makeFilesystemDropdown ($;$$$) { my ($system, $label, $default, $onchange) = @_; $label ||= ''; - + my %filesystems; - + foreach ($clearadm->FindFilesystem ($system)) { my %filesystem = %{$_}; - + my $value = "$filesystem{filesystem} ($filesystem{mount})"; $filesystems{$filesystem{filesystem}} = $value; } # foreach - + my $dropdown .= "$label "; $dropdown .= popup_menu { name => 'filesystem', @@ -397,15 +397,15 @@ sub makeFilesystemDropdown ($;$$$) { onChange => ($onchange) ? $onchange : '', default => $default, }; - - return span {id => 'filesystems'}, $dropdown; + + return span {id => 'filesystems'}, $dropdown; } # makeFilesystemDropdown sub makeIntervalDropdown (;$$$) { my ($label, $default, $onchange) = @_; - + $label ||= ''; - + my @intervals = ( 'Minute', 'Hour', @@ -415,7 +415,7 @@ sub makeIntervalDropdown (;$$$) { $default = ucfirst lc $default if $default; - + my $dropdown = "$label "; $dropdown .= popup_menu { name => 'scaling', @@ -425,49 +425,49 @@ sub makeIntervalDropdown (;$$$) { default => $default, onchange => $onchange, }; - - return span {id => 'scaling'}, $dropdown; + + return span {id => 'scaling'}, $dropdown; } # makeIntervalDropdown; sub makeNotificationDropdown (;$$) { my ($label, $default) = @_; - + $label ||= ''; my @values; - - push @values, $$_{name} + + push @values, $$_{name} foreach ($clearadm->FindNotification); - + my $dropdown = "$label "; $dropdown .= popup_menu { name => 'notification', class => 'dropdown', values => [sort @values], default => $default, - }; - + }; + return $dropdown; } # makeNotificationDropdown sub makeRestartableDropdown (;$$) { my ($label, $default) = @_; - + $label ||= ''; my @values = ( 'true', 'false', ); - + my $dropdown = "$label "; $dropdown .= popup_menu { name => 'restartable', class => 'dropdown', values => [@values], default => $default, - }; - + }; + return $dropdown; } # makeRestartableDropdown @@ -475,16 +475,16 @@ sub makeSystemDropdown (;$$$%) { my ($label, $default, $onchange, %systems) = @_; $label ||= ''; - + foreach ($clearadm->FindSystem) { my %system = %{$_}; - + my $value = $system{name}; $value .= $system{alias} ? " ($system{alias})" : ''; $systems{$system{name}} = $value; } # foreach - + my $systemDropdown .= "$label "; $systemDropdown .= popup_menu { name => 'system', @@ -494,28 +494,28 @@ sub makeSystemDropdown (;$$$%) { onchange => ($onchange) ? $onchange : '', default => $default, }; - + return span {id => 'systems'}, $systemDropdown; } # makeSystemDropdown sub makeTaskDropdown (;$$) { my ($label, $default) = @_; - + $label ||= ''; my @values; push @values, $$_{name} foreach ($clearadm->FindTask); - + my $taskDropdown = "$label "; $taskDropdown .= popup_menu { name => 'task', class => 'dropdown', values => [sort @values], default => $default, - }; - + }; + return $taskDropdown; } # makeTaskDropdown @@ -532,11 +532,11 @@ sub makeTimeDropdown ($$$;$$$$$) { ) = @_; $label ||= ''; - + my @times; - + $name ||= lc $label; - + push @times, 'Earliest'; if ($table =~ /loadavg/i) { @@ -545,14 +545,14 @@ sub makeTimeDropdown ($$$;$$$$$) { } elsif ($table =~ /filesystem/i) { push @times, $$_{timestamp} foreach ($clearadm->GetFS ($system, $filesystem, undef, undef, undef, $interval)); - } # if + } # if push @times, 'Latest'; - + unless ($default) { $default = $name eq 'start' ? 'Earliest' : 'Latest'; } # unless - + my $timeDropdown = "$label "; $timeDropdown .= span {id => $elementID}, popup_menu { name => $name, @@ -560,8 +560,8 @@ sub makeTimeDropdown ($$$;$$$$$) { values => [@times], default => $default, }; - - return $timeDropdown; + + return $timeDropdown; } # makeTimeDropdown sub heading (;$$) { @@ -572,7 +572,7 @@ sub heading (;$$) { } else { $title = $APPNAME; } # if - + display header; display start_html { -title => $title, @@ -589,21 +589,21 @@ sub heading (;$$) { }, $title; return if $type; - + my $ieTableWrapStart = ''; my $ieTableWrapEnd = ''; - + # Menubar display div {id=>'mastheadlogo'}, h1 {class => 'title'}, $APPNAME; display start_div {class => 'menu'}; - + # Home display ul li a {href => '/clearadm'}, 'Home'; - + my @allSystems = $clearadm->FindSystem; - + # Systems display start_ul; display start_li; @@ -614,7 +614,7 @@ sub heading (;$$) { my $sysName = ucfirst $system{name}; $sysName .= " ($system{alias})" if $system{alias}; - + display li a { href => "systemdetails.cgi?system=$system{name}" }, ucfirst " $sysName"; @@ -624,7 +624,7 @@ sub heading (;$$) { display end_li; display end_li; display end_ul; - + # Filesystems display start_ul; display start_li; @@ -635,7 +635,7 @@ sub heading (;$$) { my $sysName = ucfirst $system{name}; $sysName .= " ($system{alias})" if $system{alias}; - + display li a { href => "filesystems.cgi?system=$system{name}" }, ucfirst " $sysName"; @@ -644,7 +644,7 @@ sub heading (;$$) { display $ieTableWrapEnd; display end_li; display end_ul; - + # Servers display start_ul; display start_li; @@ -658,7 +658,7 @@ sub heading (;$$) { display end_ul; display $ieTableWrapEnd; display end_li; - + display start_li; display start_a {href => 'views.cgi'}; display "View»$ieTableWrapStart"; @@ -671,7 +671,7 @@ sub heading (;$$) { display $ieTableWrapEnd; display end_li; display end_ul; - + # Vobs display start_ul; display start_li; @@ -684,8 +684,8 @@ sub heading (;$$) { display end_ul; display $ieTableWrapEnd; display end_li; - display end_ul; - + display end_ul; + # Views display start_ul; display start_li; @@ -696,7 +696,7 @@ sub heading (;$$) { display end_ul; display $ieTableWrapEnd; display end_li; - display end_ul; + display end_ul; # Configure display start_ul; @@ -710,8 +710,8 @@ sub heading (;$$) { display end_ul; display $ieTableWrapEnd; display end_li; - display end_ul; - + display end_ul; + # Logs display start_ul; display start_li; @@ -722,8 +722,8 @@ sub heading (;$$) { display end_ul; display $ieTableWrapEnd; display end_li; - display end_ul; - + display end_ul; + # Help display start_ul; display start_li; @@ -735,15 +735,15 @@ sub heading (;$$) { display end_li; display end_ul; display end_div; - + display start_div {class => 'page'}; - + return; } # heading sub displayAlert (;$) { my ($alert) = @_; - + display start_table {cellspacing => 1}; display start_Tr; @@ -753,15 +753,15 @@ sub displayAlert (;$) { display th {class => 'labelCentered'}, 'Who'; display th {class => 'labelCentered'}, 'Category'; display end_Tr; - + foreach ($clearadm->FindAlert ($alert)) { my %alert = %{$_}; - + $alert{who} = setField $alert{who}, 'System Administrator'; - + display start_Tr; my $areYouSure = "Are you sure you want to delete the $alert{name} alert?"; - + my $actions = start_form { method => 'post', action => 'processalert.cgi', @@ -782,7 +782,7 @@ sub displayAlert (;$) { alt => 'Delete', value => 'Delete', title => 'Cannot delete predefined alert', - }; + }; $actions .= input { name => 'edit', disabled => 'true', @@ -812,10 +812,10 @@ sub displayAlert (;$) { }; } # if - display end_form; + display end_form; my $who = $alert{who}; - + if ($who =~ /^([a-zA-Z0-9._-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})$/) { $who = a {href => "mailto:$1\@$2"}, $who; } # if @@ -829,8 +829,8 @@ sub displayAlert (;$) { display end_Tr; } # foreach - display end_table; - + display end_table; + display p {class => 'center'}, a { href => 'processalert.cgi?action=Add', }, 'New alert ', img { @@ -843,18 +843,18 @@ sub displayAlert (;$) { sub displayAlertlog (%) { my (%opts) = @_; - + my $optsChanged; - + unless (($opts{oldalert} and $opts{alert} and $opts{oldalert} eq $opts{alert}) and ($opts{oldsystem} and $opts{system} and $opts{oldsystem} eq $opts{system}) and ($opts{oldnotification} and $opts{notification} and $opts{oldnotification} eq $opts{notification})) { - $optsChanged = 1; + $optsChanged = 1; } # unless - + my $condition; unless ($opts{id}) { @@ -881,7 +881,7 @@ sub displayAlertlog (%) { $opts{start} = 0; } # if - my $next = $opts{start} + $opts{page} < $total + my $next = $opts{start} + $opts{page} < $total ? $opts{start} + $opts{page} : $opts{start}; my $prev = $opts{start} - $opts{page} >= 0 @@ -896,14 +896,14 @@ sub displayAlertlog (%) { $opts .= " of $total"; display start_form { - method => 'post', + method => 'post', action => 'alertlog.cgi' }; # Hidden fields to pass along display input {name => 'prev', type => 'hidden', value => $prev}; display input {name => 'next', type => 'hidden', value => $next}; - + display input { name => 'oldalert', type => 'hidden', @@ -942,7 +942,7 @@ sub displayAlertlog (%) { disabled => 'disabled', }; } # unless - + $caption .= td {align => 'center'}, $opts; unless ($opts{id}) { @@ -1013,9 +1013,9 @@ sub displayAlertlog (%) { type => 'button', value => 'Clear All Events', onclick => "return AreYouSure('Are you sure you want to delete all alerts?');", - }; + }; display end_Tr; - + my $i = $opts{start}; foreach ($clearadm->FindAlertlog ( @@ -1026,10 +1026,10 @@ sub displayAlertlog (%) { $opts{page}, )) { my %alertlog = setFields 'N/A', %{$_}; - + display start_Tr; my %system = $clearadm->GetSystem ($alertlog{system}); - + display td {class => 'dataCentered'}, ++$i; display td {class => 'dataCentered'}, a { href => "deletealertlog.cgi?alertlogid=$alertlog{id}" @@ -1059,8 +1059,8 @@ sub displayAlertlog (%) { display end_form; - display end_table; - + display end_table; + return; } # displayAlertlog @@ -1068,7 +1068,7 @@ sub displayFilesystem ($) { my ($systemName) = @_; display start_table {cellspacing => 1, width => '98%'}; - + display start_Tr; display th {class => 'labelCentered'}, 'Action'; display th {class => 'labelCentered'}, 'Name'; @@ -1082,16 +1082,16 @@ sub displayFilesystem ($) { display th {class => 'labelCentered'}, 'Threshold'; display th {class => 'labelCentered'}, 'Usage'; display end_Tr; - + foreach ($clearadm->FindSystem ($systemName)) { my %system = %{$_}; - + %system = setFields ('N/A', %system); my $admin = ($system{email} !~ 'N/A') ? a {-href => "mailto:$system{email}"}, $system{admin} : $system{admin}; - + foreach ($clearadm->FindFilesystem ($system{name})) { my %filesystem = %{$_}; @@ -1101,9 +1101,9 @@ sub displayFilesystem ($) { my $used = autoScale $fs{used}; my $free = autoScale $fs{free}; - # TODO: Note that this percentages does not agree with df output. I'm not + # TODO: Note that this percentages does not agree with df output. I'm not # sure why. - my $usedPct = $fs{size} == 0 ? 0 + my $usedPct = $fs{size} == 0 ? 0 : sprintf ('%.0f', (($fs{reserve} + $fs{used}) / $fs{size} * 100)); @@ -1112,19 +1112,19 @@ sub displayFilesystem ($) { href => "systemdetails.cgi?system=$system{name}" }, $system{alias} : $system{alias}; - - my $class = $usedPct < $filesystem{threshold} + + my $class = $usedPct < $filesystem{threshold} ? 'data' : 'dataAlert'; - my $classRight = $usedPct < $filesystem{threshold} + my $classRight = $usedPct < $filesystem{threshold} ? 'dataRight' : 'dataRightAlert'; - my $classCentered = $usedPct < $filesystem{threshold} + my $classCentered = $usedPct < $filesystem{threshold} ? 'dataCentered' : 'dataCenteredAlert'; my $classRightTop = $usedPct < $filesystem{threshold} ? 'dataRightTop' - : 'dataRightAlertTop'; + : 'dataRightAlertTop'; display start_Tr; display start_td {class => 'dataCentered'}; @@ -1138,7 +1138,7 @@ sub displayFilesystem ($) { method => 'post', action => "processfilesystem.cgi", }; - + display input { type => 'hidden', name => 'system', @@ -1149,7 +1149,7 @@ sub displayFilesystem ($) { name => 'filesystem', value => $filesystem{filesystem}, }; - + display input { name => 'delete', type => 'image', @@ -1167,20 +1167,20 @@ sub displayFilesystem ($) { value => 'Edit', title => 'Edit', }; - + if ($filesystem{notification}) { display a { href => "alertlog.cgi?system=$filesystem{system}"}, img { src => 'alert.png', border => 0, alt => 'Alert!', - title => 'This filesystem has alerts', + title => 'This filesystem has alerts', }; - } # if - + } # if + display end_form; - - display end_td; + + display end_td; display td {class => $class}, a {-href => "systemdetails.cgi?system=$system{name}"}, $system{name}; display td {class => $class}, $alias; @@ -1189,11 +1189,11 @@ sub displayFilesystem ($) { display td {class => $class}, $filesystem{mount}; display td {class => $class}, $filesystem{fstype}; display td {class => $classCentered}, $filesystem{filesystemHist}; - display td {class => $classRightTop}, "$used ($usedPct%)
", + display td {class => $classRightTop}, "$used ($usedPct%)
", font {class => 'unknown'}, "$fs{timestamp}"; display td {class => $classRightTop}, "$filesystem{threshold}%"; display td {class => $class}, - a {href => + a {href => "plot.cgi?type=filesystem&system=$system{name}" . "&filesystem=$filesystem{filesystem}&scaling=Day&points=7" }, img { @@ -1206,13 +1206,13 @@ sub displayFilesystem ($) { } # foreach display end_table; - + return; } # displayFilesystem sub displayNotification (;$) { my ($notification) = @_; - + display start_table {cellspacing => 1}; display start_Tr; @@ -1223,14 +1223,14 @@ sub displayNotification (;$) { display th {class => 'labelCentered'}, 'Not More Than'; display th {class => 'labelCentered'}, 'Category'; display end_Tr; - + foreach ($clearadm->FindNotification ($notification)) { my %notification= setFields 'N/A', %{$_}; - + display start_Tr; my $areYouSure = "Are you sure you want to delete the $notification{name} " . 'notification?'; - + my $actions = start_form { method => 'post', action => 'processnotification.cgi', @@ -1241,7 +1241,7 @@ sub displayNotification (;$) { type => 'hidden', value => $notification{name}, }; - + if (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS) { $actions .= input { name => 'delete', @@ -1251,7 +1251,7 @@ sub displayNotification (;$) { alt => 'Delete', value => 'Delete', title => 'Cannot delete predefined notification', - }; + }; $actions .= input { name => 'edit', disabled => 'true', @@ -1280,8 +1280,8 @@ sub displayNotification (;$) { title => 'Edit', }; } # if - - display end_form; + + display end_form; display td {class => 'dataCentered'}, $actions; display td {class => 'data'}, $notification{name}; @@ -1291,15 +1291,15 @@ sub displayNotification (;$) { display td {class => 'data'}, $notification{cond}; display td {class => 'data'}, $notification{nomorethan}; display td {class => 'data'}, - (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS) + (InArray $notification{name}, @PREDEFINED_NOTIFICATIONS) ? 'Predefined' : 'User Defined'; - + display end_Tr; } # foreach display end_table; - + display p {class => 'center'}, a { href => 'processnotification.cgi?action=Add', }, 'New notification', img { @@ -1312,9 +1312,9 @@ sub displayNotification (;$) { sub displayRunlog (%) { my (%opts) = @_; - + my $optsChanged; - + unless (($opts{oldtask} and $opts{task} or $opts{oldtask} eq $opts{task}) and ($opts{oldsystem} and $opts{system} or @@ -1323,29 +1323,29 @@ sub displayRunlog (%) { $opts{oldnot} eq $opts{not}) and ($opts{oldstatus} and $opts{status} or $opts{oldstatus} eq $opts{status})) { - $optsChanged = 1; + $optsChanged = 1; } # unless - - my $condition; + + my $condition; unless ($opts{id}) { $condition = "task like '%"; $condition .= $opts{task} ? $opts{task} : ''; $condition .= "%'"; - + if ($opts{system}) { if ($opts{system} eq '') { $condition .= ' and system is null'; undef $opts{system} } elsif ($opts{system} ne 'All') { - $condition .= " and system like '%$opts{system}%'";; + $condition .= " and system like '%$opts{system}%'";; } # if } # if if (defined $opts{status}) { $condition .= ' and '; unless ($opts{not}) { - $condition .= "status=$opts{status}"; + $condition .= "status=$opts{status}"; } else { $condition .= "status<>$opts{status}"; } # unless @@ -1353,13 +1353,13 @@ sub displayRunlog (%) { } # unless my $total = $clearadm->Count ('runlog', $condition); - + $opts{start} = $opts{'nextArrow.x'} ? $opts{next} : $opts{prev}; $opts{start} ||= 0; $opts{start} = 0 if $optsChanged; - - my $next = $opts{start} + $opts{page} < $total + + my $next = $opts{start} + $opts{page} < $total ? $opts{start} + $opts{page} : $opts{start}; my $prev = $opts{start} - $opts{page} >= 0 @@ -1374,7 +1374,7 @@ sub displayRunlog (%) { $opts .= " of $total"; display start_form { - method => 'post', + method => 'post', action => 'runlog.cgi' }; @@ -1408,7 +1408,7 @@ sub displayRunlog (%) { disabled => 'disabled', }; } # unless - + $caption .= td {align => 'center'}, $opts; unless ($opts{id}) { @@ -1444,12 +1444,12 @@ sub displayRunlog (%) { display th {class => 'labelCentered'}, 'Status'; display th {class => 'labelCentered'}, 'Message'; display end_Tr; - + display start_Tr; $opts{not} ||= 'false'; display start_form { - method => 'post', + method => 'post', action => 'runlog.cgi' }; display td { @@ -1482,20 +1482,20 @@ sub displayRunlog (%) { type => 'submit', value => 'Update', }; - + display end_form; display end_Tr; my $i = $opts{start}; my $status; - + if (defined $opts{status}) { if ($opts{status} !~ /all/i) { $status = $opts{not} ne 'true' ? $opts{status} : "!$opts{status}"; } # if } # if - + foreach ($clearadm->FindRunlog ( $opts{task}, $opts{system}, @@ -1505,8 +1505,8 @@ sub displayRunlog (%) { $opts{page}, )) { my %runlog = setFields 'N/A', %{$_}; - - my $class = $runlog{status} == 0 + + my $class = $runlog{status} == 0 ? 'data' : 'dataAlert'; my $classCentered = $runlog{status} == 0 @@ -1515,7 +1515,7 @@ sub displayRunlog (%) { my $classRight = $runlog{status} == 0 ? 'dataRight' : 'dataAlertRight'; - + display start_Tr; display td {class => 'dataCentered'}, ++$i; display td {class => 'dataCentered'}, $runlog{id}; @@ -1530,16 +1530,16 @@ sub displayRunlog (%) { display td {class => 'dataCentered'}, $runlog{started}; display td {class => 'dataCentered'}, $runlog{ended}; display td {class => $classRight}, $runlog{status}; - + my $message = $runlog{message}; $message =~ s/\r\n/
/g; - + display td {class => $class, width => '50%'}, $message; display end_Tr; } # foreach display end_table; - + return; } # displayRunlog @@ -1555,14 +1555,14 @@ sub displaySchedule () { display th {class => 'labelCentered'}, 'Frequency'; display th {class => 'labelCentered'}, 'Category'; display end_Tr; - + foreach ($clearadm->FindSchedule) { my %schedule = setFields 'N/A', %{$_}; - + display start_Tr; my $areYouSure = "Are you sure you want to delete the $schedule{name} " . "schedule?"; - + my $actions = start_form { method => 'post', action => 'processschedule.cgi', @@ -1573,7 +1573,7 @@ sub displaySchedule () { type => 'hidden', value => $schedule{name}, }; - + if (InArray $schedule{name}, @PREDEFINED_SCHEDULES) { $actions .= input { name => 'delete', @@ -1583,7 +1583,7 @@ sub displaySchedule () { alt => 'Delete', value => 'Delete', title => 'Cannot delete predefined schedule', - }; + }; $actions .= input { name => 'edit', disabled => 'true', @@ -1612,9 +1612,9 @@ sub displaySchedule () { title => 'Edit', }; } # if - - display end_form; - + + display end_form; + display td {class => 'dataCentered'}, $actions; display td {class => 'dataCentered'}, checkbox { disabled => 'disabled', @@ -1629,15 +1629,15 @@ sub displaySchedule () { }, $schedule{notification}; display td {class => 'data'}, $schedule{frequency}; display td {class => 'data'}, - (InArray $schedule{name}, @PREDEFINED_SCHEDULES) - ? 'Predefined' - : 'User Defined'; - + (InArray $schedule{name}, @PREDEFINED_SCHEDULES) + ? 'Predefined' + : 'User Defined'; + display end_Tr; } # foreach display end_table; - + display p {class => 'center'}, a { href => 'processschedule.cgi?action=Add', }, 'New schedule', img { @@ -1650,17 +1650,17 @@ sub displaySchedule () { sub displaySystem ($) { my ($systemName) = @_; - + my %system = $clearadm->GetSystem ($systemName); - + unless (%system) { displayError "Nothing known about system $systemName"; return; } # unless - + my $lastheardfromClass = 'dataCentered'; my $lastheardfromData = $system{lastheardfrom}; - + my %load = $clearadm->GetLatestLoadavg ($systemName); unless ($clearadm->SystemAlive (%system)) { @@ -1676,18 +1676,18 @@ sub displaySystem ($) { my $admin = ($system{email}) ? a {-href => "mailto:$system{email}"}, $system{admin} : $system{admin}; - + $system{alias} = setField $system{alias}, 'N/A'; $system{region} = setField $system{region}, 'N/A'; display start_table {cellspacing => 1}; - + display start_Tr; my $areYouSure = 'Are you sure you want to delete this system?\n' . "Doing so will remove all records related to $system{name}" . '\nincluding filesystem records and history as well as ' . 'loadavg history.'; - + my $actions = start_form { method => 'post', action => 'processsystem.cgi', @@ -1698,7 +1698,7 @@ sub displaySystem ($) { type => 'hidden', value => $system{name}, }; - + $actions .= input { name => 'delete', type => 'image', @@ -1719,18 +1719,18 @@ sub displaySystem ($) { $actions .= checkbox { disabled => 'disabled', checked => $system{active} eq 'true' ? 1 : 0, - }; - + }; + if ($system{notification}) { $actions .= a { href => "alertlog.cgi?system=$system{name}"}, img { src => 'alert.png', border => 0, alt => 'Alert!', - title => 'This system has alerts', + title => 'This system has alerts', }; } # if - + display th {class => 'label'}, "$actions Name:"; display end_form; display td {class => 'dataCentered', colspan => 2}, $system{name}; @@ -1741,12 +1741,12 @@ sub displaySystem ($) { display th {class => 'label', colspan => 2}, 'Type:'; display td {class => 'dataCentered'}, $system{type}; display end_Tr; - + display start_Tr; display th {class => 'label'}, 'OS Version:'; - display td {class => 'data', colspan => 10}, $system{os}; + display td {class => 'data', colspan => 10}, $system{os}; display end_Tr; - + display start_Tr; display th {class => 'label'}, 'Last Contacted:'; display td { @@ -1762,23 +1762,23 @@ sub displaySystem ($) { display td {class => 'dataCentered'}, $system{loadavgHist}; display th {class => 'label'}, 'Load Avg:'; display td {class => 'data'}, - a {href => + a {href => "plot.cgi?type=loadavg&system=$system{name}&scaling=Hour&points=24" }, img { src => "plotloadavg.cgi?system=$system{name}&tiny=1", border => 0, }; - + my $description = $system{description}; $description =~ s/\r\n/
/g; - + display start_Tr; display th {class => 'label'}, 'Description:'; - display td {class => 'data', colSpan => 10}, $description; + display td {class => 'data', colSpan => 10}, $description; display end_Tr; - + display end_table; - + display p {class => 'center'}, a { href => 'processsystem.cgi?action=Add', }, 'New system', img { @@ -1786,11 +1786,11 @@ sub displaySystem ($) { border => 0, }; - display h1 {class => 'center'}, + display h1 {class => 'center'}, 'Filesystem Details: ' . ucfirst $system{name}; - + display start_table {cellspacing => 1}; - + display start_Tr; display th {class => 'labelCentered'}, 'Action'; display th {class => 'labelCentered'}, 'Filesystem'; @@ -1803,32 +1803,32 @@ sub displaySystem ($) { display th {class => 'labelCentered'}, 'Threshold'; display th {class => 'labelCentered'}, 'History'; display th {class => 'labelCentered'}, 'Usage'; - display end_Tr; - + display end_Tr; + foreach ($clearadm->FindFilesystem ($system{name})) { my %filesystem = %{$_}; - + my %fs = $clearadm->GetLatestFS ( - $filesystem{system}, + $filesystem{system}, $filesystem{filesystem} ); - + my $size = autoScale $fs{size}; my $used = autoScale $fs{used}; - my $free = autoScale $fs{free}; + my $free = autoScale $fs{free}; - # TODO: Note that this percentages does not agree with df output. I'm not + # TODO: Note that this percentages does not agree with df output. I'm not # sure why. - my $usedPct = $fs{size} == 0 ? 0 + my $usedPct = $fs{size} == 0 ? 0 : sprintf ('%.0f', (($fs{reserve} + $fs{used}) / $fs{size} * 100)); - - my $class = $usedPct < $filesystem{threshold} + + my $class = $usedPct < $filesystem{threshold} ? 'data' : 'dataAlert'; my $classCentered = $class . 'Centered'; my $classRight = $class . 'Right'; - + display start_Tr; display start_td {class => 'data'}; @@ -1841,7 +1841,7 @@ sub displaySystem ($) { method => 'post', action => 'processfilesystem.cgi', }; - + display input { type => 'hidden', name => 'system', @@ -1852,7 +1852,7 @@ sub displaySystem ($) { name => 'filesystem', value => $filesystem{filesystem}, }; - + display input { name => 'delete', type => 'image', @@ -1870,18 +1870,18 @@ sub displaySystem ($) { value => 'Edit', title => 'Edit', }; - + if ($filesystem{notification}) { display a { href => "alertlog.cgi?system=$filesystem{system}"}, img { src => 'alert.png', border => 0, alt => 'Alert!', - title => 'This filesystem has alerts', + title => 'This filesystem has alerts', }; - } # if + } # if - display end_form; + display end_form; display td {class => $class}, $filesystem{filesystem}; display td {class => $classCentered}, $filesystem{fstype}; display td {class => $class}, $filesystem{mount}; @@ -1891,8 +1891,8 @@ sub displaySystem ($) { display td {class => $classRight}, "$usedPct%"; display td {class => $classRight}, "$filesystem{threshold}%"; display td {class => $classCentered}, $filesystem{filesystemHist}; - display td {class => $classCentered}, - a {href => + display td {class => $classCentered}, + a {href => "plot.cgi?type=filesystem&system=$system{name}" . "&filesystem=$filesystem{filesystem}" . "&scaling=Day&points=7" @@ -1906,13 +1906,13 @@ sub displaySystem ($) { } # foreach display end_table; - + return; } # displaySystem sub displayTask (;$) { my ($task) = @_; - + display start_table {cellspacing => 1, width => '98%'}; display start_Tr; @@ -1924,16 +1924,16 @@ sub displayTask (;$) { display th {class => 'labelCentered'}, 'Restartable'; display th {class => 'labelCentered'}, 'Category'; display end_Tr; - + foreach ($clearadm->FindTask ($task)) { my %task = %{$_}; - + $task{system} = 'All Systems' unless $task{system}; - + display start_Tr; my $areYouSure = "Are you sure you want to delete the $task{name} task?"; - + my $actions = start_form { method => 'post', action => 'processtask.cgi', @@ -1954,7 +1954,7 @@ sub displayTask (;$) { alt => 'Delete', value => 'Delete', title => 'Cannot delete predefined task', - }; + }; $actions .= input { name => 'edit', disabled => 'true', @@ -1984,34 +1984,34 @@ sub displayTask (;$) { }; } # if - display end_form; + display end_form; display td {class => 'dataCentered'}, $actions; display td {class => 'data'}, $task{name}; - display td {class => 'data'}, $task{system}; + display td {class => 'data'}, $task{system}; display td {class => 'data'}, $task{description}; display td {class => 'data'}, $task{command}; display td {class => 'dataCentered'}, $task{restartable}; - display td {class => 'data'}, + display td {class => 'data'}, (InArray $task{name}, @PREDEFINED_TASKS) ? 'Predefined' : 'User Defined'; display end_Tr; } # foreach - display end_table; - + display end_table; + display p {class => 'center'}, a { href => 'processtask.cgi?action=Add', }, 'New task', img { src => 'add.png', border => 0, }; - + return; } # DisplayAlerts sub editAlert (;$) { my ($alert) = @_; - + display start_form ( -method => 'post', -action => 'processalert.cgi', @@ -2022,10 +2022,10 @@ sub editAlert (;$) { if ($alert) { %alert = $clearadm->GetAlert ($alert); - + return - unless %alert; - + unless %alert; + display input { name => 'oldname', type => 'hidden', @@ -2034,21 +2034,21 @@ sub editAlert (;$) { } else { $alert= ''; } # if - + display input { name => 'action', type => 'hidden', value => 'Post', }; - + display start_table {cellspacing => 1}; - + display start_Tr; display th {class => 'labelCentered'}, 'Name'; display th {class => 'labelCentered'}, 'Type'; display th {class => 'labelCentered'}, 'Who'; display end_Tr; - + display start_Tr; display td { class => 'data', @@ -2080,26 +2080,26 @@ sub editAlert (;$) { }; display end_Tr; display end_table; - + display '
'; display p submit ({value => $alert ? 'Update' : 'Add'}), reset; display '
'; - + display end_form; - + return; } # editAlert sub editFilesystem ($$) { my ($system, $filesystem) = @_; - + display start_form ( -method => 'post', -action => 'processfilesystem.cgi', ); display start_table {width => '800px', cellspacing => 1}; - + display start_Tr; display th {class => 'labelCentered'}, 'Filesystem'; display th {class => 'labelCentered'}, 'Type'; @@ -2110,11 +2110,11 @@ sub editFilesystem ($$) { display th {class => 'labelCentered'}, 'Used %'; display th {class => 'labelCentered'}, 'History'; display th {class => 'labelCentered'}, 'Threshold'; - display end_Tr; - + display end_Tr; + my %filesystem = $clearadm->GetFilesystem ($system, $filesystem); my %fs = $clearadm->GetLatestFS ($system, $filesystem); - + display input { name => 'action', type => 'hidden', @@ -2124,13 +2124,13 @@ sub editFilesystem ($$) { name => 'system', type => 'hidden', value => $filesystem{system}, - }; + }; display input { name => 'filesystem', type => 'hidden', value => $filesystem{filesystem}, - } ; - + } ; + my $size = autoScale $fs{size}; my $used = autoScale $fs{used}; my $free = autoScale $fs{free}; @@ -2142,11 +2142,11 @@ sub editFilesystem ($$) { display td {class => 'dataRight'}, $size; display td {class => 'dataRight'}, $used; display td {class => 'dataRight'}, $free; - # TODO: Note that this percentages does not agree with df output. I'm not + # TODO: Note that this percentages does not agree with df output. I'm not # sure why. display td {class => 'dataCentered'}, sprintf ('%.0f%%', (($fs{reserve} + $fs{used}) / $fs{size} * 100)); - + my $historyDropdown = popup_menu { name => 'filesystemHist', class => 'dropdown', @@ -2166,34 +2166,34 @@ sub editFilesystem ($$) { ], default => $system ? $filesystem{filesystemHist} : '6 months', }; - + display td { class => 'dataRight', - }, $historyDropdown; - + }, $historyDropdown; + my $thresholdDropdown = popup_menu { name => 'threshold', class => 'dropdown', values => [1 .. 100], default => $filesystem{threshold}, - }; + }; display td {class => 'dataCentered'}, $thresholdDropdown . '%'; display end_Tr; - - display end_table; - + + display end_table; + display '
'; display p submit ({value => 'Update'}), reset; display '
'; - + display end_form; - + return; } # editFilesytem sub editNotification (;$) { my ($notification) = @_; - + display start_form ( -method => 'post', -action => 'processnotification.cgi', @@ -2204,10 +2204,10 @@ sub editNotification (;$) { if ($notification) { %notification = $clearadm->GetNotification ($notification); - + return - unless %notification; - + unless %notification; + display input { name => 'oldname', type => 'hidden', @@ -2216,22 +2216,22 @@ sub editNotification (;$) { } else { $notification = ''; } # if - + display input { name => 'action', type => 'hidden', value => 'Post', }; - + display start_table {cellspacing => 1}; - + display start_Tr; display th {class => 'labelCentered'}, 'Name'; display th {class => 'labelCentered'}, 'Alert'; display th {class => 'labelCentered'}, 'Condition'; display th {class => 'labelCentered'}, 'Not More Than'; display end_Tr; - + display start_Tr; display td { class => 'data', @@ -2243,13 +2243,13 @@ sub editNotification (;$) { type => 'text', value => $notification ? $notification{name} : '', }; - + display td { class => 'dataCentered', - }, makeAlertDropdown undef, $notification{alert} + }, makeAlertDropdown undef, $notification{alert} ? $notification{alert} : 'Email admin'; - + display td { class => 'data', }, input { @@ -2263,22 +2263,22 @@ sub editNotification (;$) { display td { class => 'dataCentered', }, makeNoMoreThanDropdown undef, $notification{nomorethan}; - + display end_Tr; display end_table; - + display '
'; display p submit ({value => $notification ? 'Update' : 'Add'}), reset; display '
'; - + display end_form; - + return; } # editNotification sub editSchedule (;$) { my ($schedule) = @_; - + display start_form ( -method => 'post', -action => 'processschedule.cgi', @@ -2289,10 +2289,10 @@ sub editSchedule (;$) { if ($schedule) { %schedule = $clearadm->GetSchedule ($schedule); - + return - unless %schedule; - + unless %schedule; + display input { name => 'oldname', type => 'hidden', @@ -2301,15 +2301,15 @@ sub editSchedule (;$) { } else { $schedule = ''; } # if - + display input { name => 'action', type => 'hidden', value => 'Post', }; - + display start_table {cellspacing => 1}; - + display start_Tr; display th {class => 'labelCentered'}, 'Active'; display th {class => 'labelCentered'}, 'Name'; @@ -2317,7 +2317,7 @@ sub editSchedule (;$) { display th {class => 'labelCentered'}, 'Notification'; display th {class => 'labelCentered'}, 'Frequency'; display end_Tr; - + display start_Tr; display td { class => 'dataCentered', @@ -2339,21 +2339,21 @@ sub editSchedule (;$) { }; display td { class => 'dataCentered', - }, makeTaskDropdown undef, $schedule{task}; + }, makeTaskDropdown undef, $schedule{task}; display td { class => 'dataCentered', - }, makeNotificationDropdown undef, $schedule{notification}; - + }, makeNotificationDropdown undef, $schedule{notification}; + my $nbr = 5; my $multiplier = 'minutes'; - + if ($schedule{frequency} =~ /(\d+)\s(\S+)/ ) { $nbr = $1; $multiplier = $2; - + $multiplier .= 's' if $nbr == 1; } # if - + display td { class => 'data', }, input { @@ -2363,25 +2363,25 @@ sub editSchedule (;$) { size => 1, type => 'text', value => $nbr, - }, + }, ' ', makeMultiplierDropdown undef, $multiplier; - + display end_Tr; display end_table; - + display '
'; display p submit ({value => $schedule ? 'Update' : 'Add'}), reset; display '
'; - + display end_form; - + return; } # editSchedule sub editSystem (;$) { my ($system) = @_; - + display start_form ( -method => 'post', -action => 'processsystem.cgi', @@ -2389,13 +2389,13 @@ sub editSystem (;$) { ); my %system; - + if ($system) { %system = $clearadm->GetSystem ($system); - + return unless %system; - + display input { name => 'name', type => 'hidden', @@ -2404,15 +2404,15 @@ sub editSystem (;$) { } else { $system = ''; } # if - + display input { name => 'action', type => 'hidden', value => 'Post', }; - + display start_table {cellspacing => 1}; - + display start_Tr; display th {class => 'label'}, checkbox ({ name => 'active', @@ -2420,7 +2420,7 @@ sub editSystem (;$) { checked => $system{active} eq 'false' ? 0 : 1, label => '', }) . ' Name: '; - + if ($system) { display td {class => 'data'}, $system{name}; } else { @@ -2434,7 +2434,7 @@ sub editSystem (;$) { type => 'text', }; } # if - + display th {class => 'label'}, 'Alias:'; display td { class => 'data', @@ -2456,24 +2456,24 @@ sub editSystem (;$) { name => 'port', size => 4, type => 'text', - value => $system + value => $system ? $system{port} : $Clearadm::CLEAROPTS{CLEARADM_PORT}, }; - + my $systemTypeDropdown = popup_menu { name => 'type', class => 'dropdown', values => ['Unix', 'Linux', 'Windows'], default => $system ? $system{type} : 'Linux', }; - + display th {class => 'label'}, 'Type:'; display td { class => 'dataRight', }, $systemTypeDropdown; display end_Tr; - + display start_Tr; display th {class => 'label'}, 'Admin:'; display td { @@ -2507,11 +2507,11 @@ sub editSystem (;$) { name => 'loadavgThreshold', size => 3, type => 'text', - value => $system + value => $system ? $system{loadavgThreshold} : $Clearadm::CLEAROPTS{CLEARADM_LOADAVG_THRESHOLD}, }; - + my $historyDropdown = popup_menu { name => 'loadavgHist', class => 'dropdown', @@ -2531,15 +2531,15 @@ sub editSystem (;$) { ], default => $system ? $system{loadavgHist} : '6 months', }; - + display th {class => 'label'}, 'History:'; display td { class => 'dataRight', - }, $historyDropdown; - + }, $historyDropdown; + my $description = $system ? $system{description} : ''; $description =~ s/\r\n/
/g; - + display start_Tr; display th {class => 'label'}, 'Description:'; display td { @@ -2554,19 +2554,19 @@ sub editSystem (;$) { }; display end_Tr; display end_table; - + display '
'; display p submit ({value => $system ? 'Update' : 'Add'}), reset; display '
'; - + display end_form; - + return; } # editSystem sub editTask (;$) { my ($task) = @_; - + display start_form ( -method => 'post', -action => 'processtask.cgi', @@ -2577,10 +2577,10 @@ sub editTask (;$) { if ($task) { %task = $clearadm->GetTask ($task); - + return unless %task; - + display input { name => 'oldname', type => 'hidden', @@ -2589,15 +2589,15 @@ sub editTask (;$) { } else { $task = ''; } # if - + display input { name => 'action', type => 'hidden', value => 'Post', }; - + display start_table {cellspacing => 1}; - + display start_Tr; display th {class => 'labelCentered'}, 'Name'; display th {class => 'labelCentered'}, 'System'; @@ -2605,7 +2605,7 @@ sub editTask (;$) { display th {class => 'labelCentered'}, 'Command'; display th {class => 'labelCentered'}, 'Restartable'; display end_Tr; - + display start_Tr; display td { class => 'data', @@ -2625,8 +2625,8 @@ sub editTask (;$) { 'Localhost' => 'Localhost', ), ); - - display td {class => 'data'}, $systemDropdown; + + display td {class => 'data'}, $systemDropdown; display td { class => 'data', @@ -2638,7 +2638,7 @@ sub editTask (;$) { type => 'text', value => $task ? $task{description} : '', }; - + display td { class => 'data', }, input { @@ -2653,50 +2653,50 @@ sub editTask (;$) { display td { class => 'dataCentered', }, makeRestartableDropdown undef, $task{restartable}; - + display end_Tr; display end_table; - + display '
'; display p submit ({value => $task ? 'Update' : 'Add'}), reset; display '
'; - + display end_form; - + return; } # editTask sub footing () { my $clearscm = a {-href => 'http://clearscm.com'}, 'ClearSCM, Inc.'; - + # Figure out which script by using CLEARADM_BASE. - my $script = basename (url {-absolute => 1}); + my $script = basename (url {-absolute => 1}); $script = 'index.cgi' if $script eq 'clearadm'; my $scriptFullPath = "$Clearadm::CLEAROPTS{CLEARADM_BASE}/$script"; - - my ($year, $mon, $mday, $hour, $min, $sec) = + + my ($year, $mon, $mday, $hour, $min, $sec) = ymdhms ((stat ($scriptFullPath))[9]); my $dateModified = "$mon/$mday/$year @ $hour:$min"; - + $script = a { -href => "http://clearscm.com/php/scm_man.php?file=clearadm/$script" }, $script; - + display end_div; - + display start_div {-class => 'copyright'}; display "$script: Last modified: $dateModified"; display br "Copyright © $year, $clearscm - All rights reserved"; display end_div; - + print end_html; - + return; } # footing - + 1; =pod @@ -2725,13 +2725,13 @@ L =head2 ClearSCM Perl Modules -=begin man +=begin man Clearadm DateUtils Display Utils - + =end man =begin html diff --git a/clearadm/lib/Clearexec.pm b/clearadm/lib/Clearexec.pm index 27371c9..410a94e 100644 --- a/clearadm/lib/Clearexec.pm +++ b/clearadm/lib/Clearexec.pm @@ -38,7 +38,7 @@ The results are sent back as follows: Status: - + This allows the caller to determine if the command execution was successful as well as capture the commands output. @@ -282,7 +282,7 @@ sub _serviceClient ($$) { $self->_debug ("Status: $status"); } # if - print $client "$_\n" foreach (@output); + print $client "$_\n" for (@output); print $client "Clearexec Status: $status\n"; $self->_debug ("Looping around for next command"); @@ -319,7 +319,7 @@ sub startServer (;$) { while () { $client = $self->{socket}->accept; - + if ($? == -1) { if ($!{EINTR}) { next; @@ -394,7 +394,7 @@ L =head2 ClearSCM Perl Modules -=begin man +=begin man DateUtils Display diff --git a/clearadm/lib/User.pm b/clearadm/lib/User.pm index 9a0605a..2203c29 100644 --- a/clearadm/lib/User.pm +++ b/clearadm/lib/User.pm @@ -31,16 +31,16 @@ $Date: 2011/01/09 01:03:10 $ This module implements a User object which returns information about a user. my $user = new User ('adefaria'); - + print "Fullname: $user->{fullname}\n"; print "EMail: $user->{email}\n"; - + =head2 DESCRIPTION -This module instanciates a user object for the given user identifier and +This module instanciates a user object for the given user identifier and then collects information about the user such as fullname, email, etc. It does so by contacting Active Directory in a Windows domain or other directory servers -depending on the site. As such exactly what data members are available may +depending on the site. As such exactly what data members are available may change or be different from site to site. =cut @@ -60,7 +60,7 @@ our %CLEAROPTS= GetConfig ("$FindBin::Bin/etc/clearuser.conf"); our $VERSION = '$Revision: 1.4 $'; ($VERSION) = ($VERSION =~ /\$Revision: (.*) /); - + # Override options if in the environment $CLEAROPTS{CLEARUSER_LDAPHOST} = $ENV{CLEARUSER_LDAPHOST} if $ENV{CLEARUSER_LDAPHOST}; @@ -91,7 +91,7 @@ sub unix2sso ($) { sub GetOwnerInfo ($) { my ($userid) = @_; - + my @parts = split /(\/|\\)/, $userid; if (@parts == 3) { @@ -99,28 +99,28 @@ sub GetOwnerInfo ($) { } # if my $sso = unix2sso ($userid); - + unless ($ldap) { $ldap = Net::LDAP->new ($CLEAROPTS{CLEARUSER_LDAPHOST}) or croak 'Unable to create LDAP object'; - + $ad = $ldap->bind ( "$CLEAROPTS{CLEARUSER_USERNAME}\@$CLEAROPTS{CLEARUSER_BIND}", password => $CLEAROPTS{CLEARUSER_PASSWORD}); } # unless - + $ad = $ldap->search ( base => $CLEAROPTS{CLEARUSER_BASEDN}, filter => "(&(objectclass=user)(sAMAccountName=$sso))", ); - - $ad->code + + $ad->code && croak $ad->error; - + my @entries = $ad->entries; my %ownerInfo; - + if (@entries == 1) { for (my $i = 0; $i < $ad->count; $i++) { my $entry = $ad->entry ($i); @@ -129,11 +129,11 @@ sub GetOwnerInfo ($) { $ownerInfo{$attribute} = $entry->get_value ($attribute) } # foreach } # for - + return %ownerInfo; } else { return; - } # if + } # if } # GetOwnerInfo =pod @@ -191,16 +191,16 @@ sub new ($) { croak "Must specify userid to User constructor" if @_ == 1; - + my %members; - + $members{id} = $userid; - + my %ownerInfo = GetOwnerInfo ($userid); - + $members{$_} = $ownerInfo{$_} foreach (keys %ownerInfo); - + return bless \%members, $class; } # new @@ -226,7 +226,7 @@ L =head2 ClearSCM Perl Modules -=begin man +=begin man GetConfig diff --git a/data/machines b/data/machines index 7d9873e..83d6961 100644 --- a/data/machines +++ b/data/machines @@ -11,28 +11,28 @@ # Column 4 ClearCase Version (if applicable) # Column 5 Owner (if known) # Column 6 Usage (if known) -chargers:Sun:Solaris 5.9:70.1.1:ccadm:ranview1 +chargers:Sun:Solaris 5.9:7.0.1.1:ccadm:ranview1 colts:Sun:Solaris 5.9:7.0.1.0:ccadm:ranview2 cowboys:Sun:Solaris 5.9:7.0.1.0:ccadm:ranvob1/registry server -niners:Sun:Solaris 5.9:2003.06.10+:ccadm:? -patriots:Sun:?:Solaris 5.9:7.0.1.0:ccadm:ranvob2 +niners:Sun:Solaris 5.9:2003.06.10+:ccadm: +patriots:Sun:Solaris 5.9:7.0.1.0:ccadm:ranvob2 rams:Sun:Solaris 5.9:7.0.1.0:ccadm:ranvob3/registry server -#ranadm1:Sun:Solaris 5.9:?:ccadm:Jumpstart, PowerBroker, NIS, SMTP, DNS, NTP +#ranadm1:Sun:Solaris 5.9::ccadm:Jumpstart, PowerBroker, NIS, SMTP, DNS, NTP ranadm2:Sun:Solaris 5.9:2003.06.10+:ccadm:License server, Sys admin, NIS, Home Directory server -ranbkp2:?:?:?:ccadm:? +ranbkp2::::ccadm:Backup ranbuild1:Sun:Solaris 5.8:7.0.1.0:ccadm:builds ranbuild2:Sun:Solaris 5.9:2003.06.10+:ccadm:builds ranbuild4:Sun:Solaris 5.9:7.0.1.1:ccadm:builds -rancpp01:Redhat Linux:2.6.18.53.Eel5xen:?:ccadm:? -rancpp02:Redhat Linux:2.6.18.53.Eel5xen:?:ccadm:? -rancpp03:Redhat Linux:2.6.18.53.Eel5xen:?:ccadm:? -rancpp10:Redhat Linux:2.6.18.53.Eel5xen:?:ccadm:? -#randbs:Sun:Solaris 5.9:?:ccadm:CQ DB server/Bldforge +rancpp01:Redhat Linux:2.6.18.53.Eel5xen::ccadm: +rancpp02:Redhat Linux:2.6.18.53.Eel5xen::ccadm: +rancpp03:Redhat Linux:2.6.18.53.Eel5xen::ccadm: +rancpp10:Redhat Linux:2.6.18.53.Eel5xen::ccadm: +#randbs:Sun:Solaris 5.9::ccadm:CQ DB server/Bldforge randws094:Sun:Solaris 5.10:7.0.1.1:John Hartin:Old workstation -randws103:Sun:Solaris 5.9:7.0.1.1:?:? -randws106:Sun:Solaris 5.9:2003.06.10+:?:? -randws113:Sun:Solaris 5.9:7.0.1.1:?:? -randws114:Sun:Solaris 5.9:2003.06.10+:Tony Trujilo:? +randws103:Sun:Solaris 5.9:7.0.1.1:: +randws106:Sun:Solaris 5.9:2003.06.10+:: +randws113:Sun:Solaris 5.9:7.0.1.1:: +randws114:Sun:Solaris 5.9:2003.06.10+:Tony Trujilo: randws119:Sun:Solaris 5.9:7.0.1.1:Hy Truong:Workstation -ranlin03:Redhat Linux:2.4.21-50.Elsmp:?:ccadm:? -ranray:Sun:Solaris 5.9:2003.06.10+:ccadm:? +ranlin03:Redhat Linux:2.4.21-50.Elsmp::ccadm: +ranray:Sun:Solaris 5.9:2003.06.10+:ccadm: diff --git a/etc/machines.sql b/etc/machines.sql new file mode 100644 index 0000000..3691e75 --- /dev/null +++ b/etc/machines.sql @@ -0,0 +1,62 @@ +-- ----------------------------------------------------------------------------- +-- +-- File: $RCSfile: Machines.sql,v $ +-- Revision: $Revision: 1.$ +-- Description: Create the Machines database +-- Author: Andrew@DeFaria.com +-- Created: Fri, Jul 13, 2018 10:51:18 AM +-- Modified: $Date: $ +-- Language: SQL +-- +-- Copyright (c) 2010, ClearSCM, Inc., all rights reserved +-- +-- ----------------------------------------------------------------------------- +-- Warning: The following line will delete the old database! +-- drop database if exists machines; + +-- Create a new database +create database machines; + +-- Now let's focus on this new database +use machines; + +-- system: Define what makes up a system or machine +create table system ( + name varchar (255) not null, + model tinytext, + alias varchar (255), + active enum ( + 'true', + 'false' + ) not null default 'true', + admin tinytext, + email tinytext, + os tinytext, + ccver tinytext, + type enum ( + 'Linux', + 'Unix', + 'Windows', + 'Mac' + ) not null, + lastheardfrom datetime, + description text, + + primary key (name) +) engine=innodb; -- system + +-- package: A package is any software package that we wish to keep track of +create table package ( + system varchar (255) not null, + name varchar (255) not null, + version tinytext not null, + vendor tinytext, + description text, + + key packageIndex (name), + key systemIndex (system), + foreign key systemLink (system) references system (name) + on delete cascade + on update cascade, + primary key (system, name) +) engine=innodb; -- package diff --git a/lib/Clearquest/DBService.pm b/lib/Clearquest/DBService.pm index 6693f14..b1c73c0 100644 --- a/lib/Clearquest/DBService.pm +++ b/lib/Clearquest/DBService.pm @@ -30,7 +30,7 @@ $Date: 2011/12/31 02:13:37 $ Provides an interface to the Clearquest database over the network. -This library implements both the daemon portion of the server and the client +This library implements both the daemon portion of the server and the client API. =head1 DESCRIPTION @@ -42,7 +42,7 @@ write to the Clearquest database for write access to succeed. A hash is passed into to the execute method, which the client should use to talk to the server, that describes relatively simple protocol to tell the server what action to perform. In both the read case and the read/write case a field named -id should be defined that has a value of "=" (e.g. +id should be defined that has a value of "=" (e.g. "defect=BUGDB00034429"). For the read case the rest of the keys are the names of the fields to retrieve @@ -50,7 +50,7 @@ with values that are undef'ed. For read/write, the rest of hash contains name value pairs of fields to set and their values. Execute returns a status and a hash of name value pairs for the read case and an -array of lines for any error messages for the read/write case. +array of lines for any error messages for the read/write case. =head1 ROUTINES @@ -83,7 +83,7 @@ our %OPTS = GetConfig $config; our $VERSION = '$Revision: 1.2 $'; ($VERSION) = ($VERSION =~ /\$Revision: (.*) /); - + # Override options if in the environment $OPTS{CQD_HOST} = $ENV{CQD_HOST} if $ENV{CQD_HOST}; @@ -116,31 +116,31 @@ sub _tag ($) { my $tag = YMDHMS; $tag .= ' '; $tag .= $self->{pid} ? "[$self->{pid}] " : ''; - + return "$tag$msg"; } # _tag -sub _verbose ($) { + sub _verbose ($) { my ($self, $msg) = @_; verbose $self->_tag ($msg); - + return; } # _verbose sub _debug ($) { my ($self, $msg) = @_; - + debug $self->_tag ($msg); - + return; } # _debug sub _log ($) { my ($self, $msg) = @_; - + display $self->_tag ($msg); - + return; } # log @@ -149,9 +149,9 @@ sub _funeral () { while (my $childpid = waitpid (-1, WNOHANG) > 0) { my $status = $?; - + debug "childpid: $childpid - status: $status"; - + if ($childpid != -1) { local $SIG{CHLD} = \&_funeral; @@ -164,21 +164,21 @@ sub _funeral () { debug "All children reaped"; } # if } # while - + return; } # _funeral sub _endServer () { display "CQDService V$VERSION shutdown at " . localtime; - + # Kill process group kill 'TERM', -$$; - + # Wait for all children to die while (wait != -1) { # do nothing - } # while - + } # while + # Now that we are alone, we can simply exit exit; } # _endServer @@ -186,7 +186,7 @@ sub _endServer () { sub _restartServer () { # Not sure what to do on a restart server display 'Entered _restartServer'; - + return; } # _restartServer @@ -211,7 +211,7 @@ sub connectToServer (;$$) { $host ||= $OPTS{CQD_HOST}; $port ||= $OPTS{CQD_PORT}; - + $self->{socket} = IO::Socket::INET->new ( Proto => 'tcp', PeerAddr => $host, @@ -219,7 +219,7 @@ sub connectToServer (;$$) { ); return unless $self->{socket}; - + $self->{socket}->autoflush; $self->{host} = $host; @@ -233,10 +233,10 @@ sub disconnectFromServer () { if ($self->{socket}) { close $self->{socket}; - + undef $self->{socket}; } # if - + return; } # disconnectFromServer @@ -249,7 +249,7 @@ sub _serviceClient ($$) { # Set autoflush for client $client->autoflush if $client; - + # Input is simple and consists of the following: # # = @@ -260,69 +260,69 @@ sub _serviceClient ($$) { # # Notes: can be . Also a += means append this fieldvalue to # the existing value for the field. - + # First get record line my $line = <$client>; - + if ($line) { chomp $line; chop $line if $line =~ /\r$/; } else { $self->_verbose ("Host $host went away!"); - + close $client; - + return; } # if - + if ($line =~ /stopserver/i) { if ($self->{server}) { $self->_verbose ("$host requested to stop server [$self->{server}]"); - + # Send server hangup signal kill 'HUP', $self->{server}; } else { $self->_verbose ('Shutting down server'); - + print $client "CQDService Status: 0\n"; - + exit; } # if } # if my ($record, $id) = split /=/, $line; - + unless ($id) { $self->_verbose ('Garbled record line - rejected request'); - + close $client; - + return; } # unless - + $self->_verbose ("Client wishes to deal with $id"); - + my $scope; - + if ($id =~ /_(\S+)/) { $scope = $1; } # if - + $self->_debug ("$host wants $record:$id"); - + my ($read, %fields); - - # Now read name/value pairs + + # Now read name/value pairs while () { # Read command from client - $line = <$client>; - + $line = <$client>; + if ($line) { chomp $line; chop $line if $line =~ /\r$/; } else { $self->_verbose ("Host $host went away!"); - + close $client; - + return; } # if @@ -331,86 +331,86 @@ sub _serviceClient ($$) { # Collect name/values. Note if only names are requested then we will instead # return data. my ($name, $value) = split /=/, $line; - + if ($value) { # Transform %0A's back to \n $value =~ s/\%0A/\n/g; - + $self->_verbose ("Will set $name to $value"); } else { $read = 1; $self->_verbose ("Will retrieve $name"); - } # if - + } # if + $fields{$name} = $value; } # while - + # Get record my $entity; - + $self->_verbose ("Getting $record:$id"); - + eval { $entity = $self->{session}->GetEntity ($record, $id) }; - + unless ($entity) { print $client "Unable to GetEntity $record:$id\n"; - + close $client; - + return; } # unless if ($read) { print $client "$_@@" . $entity->GetFieldValue ($_)->GetValue . "\n" - foreach (keys %fields); + for (keys %fields); print $client "CQD Status: 0\n"; - + close $client; - + return; } # if - + # Edit record $self->_verbose ("Editing $id"); - + $entity->EditEntity ('Backend'); - + my $status; - - foreach my $fieldName (keys %fields) { + + for my $fieldName (keys %fields) { if ($fieldName =~ /(.+)\*$/) { my $newValue = delete $fields{$fieldName}; $fieldName = $1; - + $fields{$fieldName} = $entity->GetFieldValue ($fieldName)->GetValue . $newValue; } # if $self->_verbose ("Setting $fieldName to $fields{$fieldName}"); - + $status = $entity->SetFieldValue ($fieldName, $fields{$fieldName}); - + if ($status ne '') { $self->_verbose ($status); - + print $client "$status\n"; print $client "CQD Status: 1\n"; - + close $client; - + return; } # if - } # foreach - + } # for + $self->_verbose ("Validating $id"); - + $status = $entity->Validate; - + if ($status eq '') { $self->_verbose ('Committing'); $entity->Commit; - + print $client "Successfully updated $id\n"; print $client "CQD Status: 0\n"; } else { @@ -419,66 +419,66 @@ sub _serviceClient ($$) { print $client "$status\n"; print $client "CQD Status: 1\n"; } # if - + close $client; - + $self->_verbose ("Serviced requests from $host"); - + return; } # _serviceClient sub execute (%) { my ($self, %request) = @_; - + $self->connectToServer or croak 'Unable to connect to CQD Service'; return (-1, 'Unable to talk to server') unless $self->{socket}; - + my ($status, @output) = (-1, ()); - + my $server = $self->{socket}; - + my $id = delete $request{id}; - + print $server "$id\n"; - + my $read; - - foreach (keys %request) { + + for (keys %request) { if ($request{$_}) { print $server "$_=$request{$_}\n"; } else { $read = 1; print $server "$_\n"; } # if - } # foreach + } # for print $server "end\n"; - + my ($response, %output); - + while (defined ($response = <$server>)) { if ($response =~ /CQD Status: (-*\d+)/) { $status = $1; last; } # if - + if ($read) { chomp $response; chop $response if $response =~ /\r$/; - + my ($field, $value) = split /\@\@/, $response; - + $output{$field} = $value; } else { push @output, $response; } # if } # while - + chomp @output unless $read; - + $self->disconnectFromServer; - + if ($status != 0 or $read == 0) { return ($status, @output); } else { @@ -487,9 +487,9 @@ sub execute (%) { } # execute sub startServer (;$$$$$) { - + require 'Clearquest.pm'; - + my ($self, $port, $username, $password, $db, $dbset) = @_; $port ||= $OPTS{CQD_PORT}; @@ -497,7 +497,7 @@ sub startServer (;$$$$$) { $password ||= $OPTS{CQD_PASSWORD}; $db ||= $OPTS{CQD_DATABASE}; $dbset ||= $OPTS{CQD_DBSET}; - + # Create new socket to communicate to clients with $self->{socket} = IO::Socket::INET->new( Proto => 'tcp', @@ -518,7 +518,7 @@ sub startServer (;$$$$$) { # Announce ourselves $self->_log ("CQD V$VERSION accepting clients at " . localtime); - + # Now wait for an incoming request LOOP: my $client; @@ -538,7 +538,7 @@ sub startServer (;$$$$$) { error "Can't fork: $!" unless defined ($childpid = fork); - + if ($childpid) { $self->{pid} = $$; @@ -600,7 +600,7 @@ L =head2 ClearSCM Perl Modules -=begin man +=begin man DateUtils Display diff --git a/lib/Clearquest/REST.pm b/lib/Clearquest/REST.pm index 74b0bd8..2d11280 100644 --- a/lib/Clearquest/REST.pm +++ b/lib/Clearquest/REST.pm @@ -151,7 +151,7 @@ Database Set name (Default: From cq.conf) =back =cut - + our (%RECORDS, %FIELDS); # FieldTypes ENUM @@ -169,14 +169,14 @@ my $RECORD_TYPE = 9; sub _callREST ($$$;%) { my ($self, $type, $url, $body, %parms) = @_; - + # Set error and errmsg to no error $self->error (0); $self->{errmsg} = ''; - + # Upshift the call type as the calls are actually like 'GET' and not 'get' $type = uc $type; - + # We only support these call types croak "Unknown call type \"$type\"" unless $type eq 'GET' or @@ -186,7 +186,7 @@ sub _callREST ($$$;%) { $type eq 'PUT' or $type eq 'DELETE' or $type eq 'HEAD'; - + # If the caller did not give us authorization then use the login member we # already have in the object unless ($parms{Authorization}) { @@ -198,10 +198,10 @@ sub _callREST ($$$;%) { if ($url =~ /oslc.where/) { $parms{'OSLC-Core-Version'} = '2.0'; } # if - + # Remove the host portion if any $url =~ s/^http.*$self->{server}//; - + # Call the REST call (Different calls have different numbers of parameters) if ($type eq 'GET' or $type eq 'DELETE' or @@ -211,29 +211,29 @@ sub _callREST ($$$;%) { } else { $self->{rest}->$type ($url, $body, \%parms); } # if - + return $self->error; } # _callREST sub _getRecordName ($) { my ($self, $query) = @_; - + $self->_callREST ('get', $query); - + if ($self->error) { $self->errmsg ("Unable to get record name for $query"); - + return; } # if my %record = %{XMLin ($self->{rest}->responseContent)}; - + return $record{element}{name}; } # _getRecordName sub _getAttachmentList ($$) { my ($self, $result, $fields) = @_; - + croak ((caller(0))[3] . ' is not implemented'); return; @@ -247,14 +247,14 @@ sub _getInternalID ($$) { $query .= "rcm.name=$key"; $self->_callREST ('get', $query); - + unless ($self->error) { my %result = %{XMLin ($self->{rest}->responseContent)}; return $result{entry}{id}; } else { $self->errmsg ("Record not found (Table: $table, Key: \"$key\")"); - + return $self->errmsg; } # unless } # _getInternalID @@ -263,21 +263,21 @@ sub _getRecord ($$@) { my ($self, $table, $url, @fields) = @_; $self->{fields} = [$self->_setFields ($table, @fields)]; - + $self->_callREST ('get', $url); - + return if $self->error; # Now parse the results my %result = %{XMLin ($self->{rest}->responseContent)}; - + if ($result{entry}{content}{$table}) { return $self->_parseFields ($table, %{$result{entry}{content}{$table}}); } elsif (ref \%result eq 'HASH') { # The if test above will create an empty $result{entry}{content}. We need # to delete that delete $result{entry}; - + return $self->_parseFields ($table, %result); } else { return; @@ -288,7 +288,7 @@ sub _getRecordID ($) { my ($self, $table) = @_; $self->records; - + return $RECORDS{$table}; } # _getRecordID @@ -296,44 +296,44 @@ sub _getRecordURL ($$;@) { my ($self, $table, $url, @fields) = @_; $self->{fields} = [$self->_setFields ($table, @fields)]; - + $self->error ($self->_callREST ('get', $url)); - + return if $self->error; - + return $self->_parseFields ($table, %{XMLin ($self->{rest}->responseContent)}); } # _getRecordURL sub _getReferenceList ($$) { my ($self, $url, $field) = @_; - + $self->error ($self->_callREST ('get', $url)); - + return if $self->error; - + my %result = %{XMLin ($self->{rest}->responseContent)}; my @values; - + # Need to find the field array here... foreach my $key (keys %result) { if (ref $result{$key} eq 'ARRAY') { foreach (@{$result{$key}}) { push @values, $$_{'oslc_cm:label'}; } # foreach - + last; } elsif (ref $result{$key} eq 'HASH' and $result{$key}{'oslc_cm:label'}) { push @values, $result{$key}{'oslc_cm:label'}; } # if } # foreach - + return @values; } # _getReferenceList sub _parseCondition ($$) { my ($self, $table, $condition) = @_; - + # Parse simple conditions only my ($field, $operator, $value); @@ -354,13 +354,13 @@ sub _parseCondition ($$) { return "$field in [$value]" } # if } # if - + if ($operator eq '=' and $value =~ /^null$/i) { return "$field in [\"\"]"; } elsif ($operator eq '!=' and $value =~ /^null$/i) { return "$field in [*]"; } # if - + # Trim quotes if any: if ($value =~ /^\s*\'/) { $value =~ s/^\s*\'//; @@ -369,17 +369,17 @@ sub _parseCondition ($$) { $value =~ s/^\s*\"//; $value =~ s/\"\s*$//; } # if - + # Trim leading and trailing whitespace $value =~ s/^\s+//; $value =~ s/\s+$//; - + # Convert datetimes to Zulu if ($self->fieldType ($table, $field) == $DATE_TIME and $value !~ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/) { $value = Clearquest::_UTCTime ($value); } # if - + return "$field $operator \"$value\""; } # _parseCondition @@ -387,28 +387,28 @@ sub _parseConditional ($$) { my ($self, $table, $condition) = @_; return 'oslc_cm.query=' unless $condition; - + my $parsedConditional; - + # Special case when the condition is ultra simple if ($condition !~ /(\w+)\s*(==|=|!=|<>|<|>|<=|>=|in|is\s+null|is\s+not\s+null)\s*(.*)/i) { return "rcm.name=$condition"; } # if - + # TODO: This section needs improvement to handle more complex conditionals while () { if ($condition =~ /(.+?)\s+(and|or)\s+(.+)/i) { my $leftSide = $self->_parseCondition ($table, $1); - + $parsedConditional .= "$leftSide $2 "; $condition = $3; } else { $parsedConditional .= $self->_parseCondition ($table, $condition); - + last; } # if } # while - + # TODO: How would this work if we have a condition like 'f1 = "value" and # f2 is not null'? if ($parsedConditional =~ /in \[\*\]/) { @@ -420,16 +420,16 @@ sub _parseConditional ($$) { sub _parseFields ($%) { my ($self, $table, %record) = @_; - + foreach my $field (keys %record) { if ($field =~ /:/ or $field eq 'xmlns' or grep {/^$field$/} @{$self->{fields}} == 0) { delete $record{$field}; - + next; } # if - + my $fieldType = $self->fieldType ($table, $field); if (ref $record{$field} eq 'HASH') { @@ -441,7 +441,7 @@ sub _parseFields ($%) { $record{$field} = \@values; } elsif ($fieldType == $ATTACHMENT_LIST) { my @attachments = $self->_getAttachmentList ($record{$field}{'oslc_cm:collref'}, $field); - + $record{$field} = \@attachments; } elsif ($fieldType == $RECORD_TYPE) { $record{$field} = $record{$field}{'oslc_cm:label'}; @@ -449,33 +449,33 @@ sub _parseFields ($%) { $record{$field} = undef; } # if } # if - + $record{$field} ||= '' if $self->{emptyStringForUndef}; if ($fieldType == $DATE_TIME) { $record{$field} = Clearquest::_UTC2Localtime $record{$field}; } # if } # foreach - + return %record; } # _parseFields sub _parseRecordDesc ($) { my ($self, $table) = @_; - + # Need to get fieldType info my $recordID = $self->_getRecordID ($table); - + return unless $recordID; - + my $url = "$self->{uri}/record-type/$recordID"; - + $self->_callREST ('get', $url); - + return if $self->error; - + my %result = %{XMLin ($self->{rest}->responseContent)}; - + # Reach in deep for field definitions my %fields = %{$result{element}{complexType}{choice}{element}}; @@ -520,15 +520,15 @@ sub _parseRecordDesc ($) { } else { $FIELDS{$table}{$_}{FieldType} = $UNKNOWN; } # if - + if ($fields{$_}{'cq:systemOwned'} and $fields{$_}{'cq:systemOwned'} eq 'true') { $FIELDS{$table}{$_}{SystemField} = 1; } else { $FIELDS{$table}{$_}{SystemField} = 0; } # if } # foreach - - return; + + return; } # _parseRecordDesc sub _isSystemField ($$) { @@ -557,17 +557,17 @@ sub _setFields ($@) { # Cause %FIELDS to be expanded for $table $self->_parseRecordDesc ($table); - + unless (@fields) { foreach ($self->fields ($table)) { unless ($self->{returnSystemFields}) { next if $FIELDS{$table}{$_}{SystemField} } # unless - + push @fields, $_; } # foreach } # unless - + push @fields, 'dbid' unless grep { /dbid/ } @fields; return @fields; @@ -577,9 +577,9 @@ sub _setFieldValue ($$$) { my ($self, $table, $fieldName, $fieldValue) = @_; return if $self->_isSystemField ($table, $fieldName); - + my $xml .= "<$fieldName>"; - + my $fieldType = $self->fieldType ($table, $fieldName); if ($fieldType == $STRING or @@ -589,39 +589,39 @@ sub _setFieldValue ($$$) { # Fix MULTILINE_STRINGs if ($fieldType == $MULTILINE_STRING and ref $fieldValue eq 'ARRAY') { chomp @{$fieldName}; - + $fieldValue= join "\n", @$fieldValue; } # if - + $xml .= escapeHTML $fieldValue; } elsif ($fieldType == $REFERENCE) { my $tableReferenced = $self->fieldReference ($table, $fieldName); - + if ($tableReferenced) { $xml .= $self->_getInternalID ($tableReferenced, $fieldValue); } else { $self->error (600); $self->errmsg ("Could not determine reference for $fieldName"); - + return; } # if } elsif ($fieldType == $REFERENCE_LIST) { # We'll allow either an array reference or a single value, which we will # turn into an array my @values; - + @values = ref $fieldValue eq 'ARRAY' ? @$fieldValue : ($fieldValue); - + my $tableReferenced = $self->fieldReference ($table, $fieldName); - + unless ($tableReferenced) { $self->error (600); $self->errmsg ("Could not determine reference for $fieldName"); - + return; } # if - + foreach (@values) { my $internalID = $self->_getInternalID ($tableReferenced, $_); @@ -630,7 +630,7 @@ sub _setFieldValue ($$$) { } else { $self->error (600); $self->errmsg ("Could not find a valid/active $tableReferenced with a key of \"$_\""); - + return } # if } # foreach @@ -639,13 +639,13 @@ sub _setFieldValue ($$$) { } # if $xml .= "\n"; - + return $xml; } # _setFieldValue sub _startXML ($) { my ($table) = @_; - + my $xml = << "XML"; <$table @@ -654,7 +654,7 @@ sub _startXML ($) { xmlns:dc="http://purl.org/dc/terms/" xmlns:oslc_cm="http://open-services.net/xmlns/cm/1.0/"> XML - + return $xml } # _startXML @@ -723,15 +723,15 @@ Error message (if any) # First process all fields in the @ordering, if specified $xml .= $self->_setFieldValue ($table, $_, $record{$_}) foreach (@ordering); - + foreach my $field (keys %record) { next if InArray $field, @ordering; - + $xml .= $self->_setFieldValue ($table, $field, $record{$field}); } # foreach - + $xml .= ""; - + $self->_callREST ('post', $uri, $xml); # Get the DBID of the newly created record @@ -744,7 +744,7 @@ Error message (if any) sub connect (;$$$$) { my ($self, $username, $password, $database, $dbset) = @_; - + =pod =head2 connect (;$$$$) @@ -795,7 +795,7 @@ Returns: if (ref $username eq 'HASH') { my %opts = %$username; - + $self->{username} = delete $opts{CQ_USERNAME}; $self->{password} = delete $opts{CQ_PASSWORD}; $self->{database} = delete $opts{CQ_DATABASE}; @@ -806,17 +806,17 @@ Returns: $self->{database} = $database if $database; $self->{dbset} = $dbset if $dbset; } # if - + # Set URI in case anything changed $self->{uri} = "/cqweb/oslc/repo/$self->{dbset}/db/$self->{database}"; $self->{loggedin} = 1; - + return 1; } # connect sub connected () { my ($self) = @_; - + =pod =head2 connected () @@ -848,7 +848,7 @@ Returns: =for html =cut - + return $self->{loggedin}; } # connected @@ -934,7 +934,7 @@ sub dbsets () { sub delete ($$) { my ($self, $table, $key) = @_; - + =pod =head2 delete ($table, $key) @@ -976,7 +976,7 @@ Error message (if any) =cut my $query = $self->_getInternalID ($table, $key); - + # Need to remove $self->{server} from beginning of $query $query =~ s/^http.*$self->{server}//; @@ -992,13 +992,13 @@ sub DESTROY () { # global destruction (like when you die or exit), the ordering of destruction # is unpredictable so we might not succeed. return unless $self->{rest}; - + # Delete session - ignore error as there's really nothing we can do if this # fails. $self->_callREST ('delete', '/cqweb/oslc/session/'); - + croak "Unable to release REST session in destructor" if $self->error; - + return; } # DESTROY @@ -1051,9 +1051,9 @@ Error number (if any) =cut return unless $self->{rest}; - + $self->_callREST ('delete', '/cqweb/oslc/session/'); - + return $self->error; } # disconnect @@ -1106,10 +1106,10 @@ Last error message return $self->{errmsg}; } else { my $response = $self->response; - + if ($response and $response ne '') { my %xml = %{XMLin ($self->response)}; - + if ($xml{Error}{message}) { $self->{errmsg} = $xml{Error}{message}; } elsif (ref $xml{message} ne 'HASH' and $xml{message}) { @@ -1122,13 +1122,13 @@ Last error message } # if } # if } # if - + return $self->{errmsg}; } # errmsg sub error (;$) { my ($self, $error) = @_; - + =pod =head2 error ($error) @@ -1164,8 +1164,7 @@ Last error =for html =cut - - + if (defined $error) { $self->{responseCode} = $error; } else { @@ -1187,7 +1186,7 @@ Last error sub fields ($) { my ($self, $table) = @_; - + =pod =head2 fields ($table) @@ -1225,19 +1224,19 @@ Array of the fields names for $table =cut my $recordID = $self->_getRecordID ($table); - + return unless $recordID; - + my $url = "$self->{uri}/record-type/$recordID"; $self->_callREST ('get', $url); - + return if $self->error; my %result = %{XMLin ($self->{rest}->responseContent)}; - + my @fields = keys %{$result{element}{complexType}{choice}{element}}; - + return @fields; } # fields @@ -1283,7 +1282,7 @@ Fieldtype enum =for html =cut - + # If we've already computed the fieldTypes for the fields in this table then # return the value if ($FIELDS{$table}) { @@ -1360,7 +1359,7 @@ this is not a reference or reference list field. sub find ($;$@) { my ($self, $table, $condition, @fields) = @_; - + =pod =head2 find ($;$@) @@ -1413,34 +1412,34 @@ is also returned. $self->{url} = "$self->{uri}/record/?rcm.type=$table&" . $self->_parseConditional ($table, $condition); - + @fields = $self->_setFields ($table, @fields); - + # Remove dbid for find @fields = grep { $_ ne 'dbid' } @fields; - + if (@fields) { $self->{url} .= "&oslc_cm.properties="; $self->{url} .= join ',', @fields; } # if - + # Save some fields for getNext $self->{fields} = \@fields; $self->{table} = $table; - + $self->{url} .= "&oslc_cm.pageSize=1"; - + return $self->{url} unless wantarray; - + # If the user wants an array then he wants ($reesult, $nbrRecs) and so we need # to go out and get that info. $self->_callREST ('get', $self->{url}); - + return (undef, 0) if $self->error; # Now parse the results my %result = %{XMLin ($self->{rest}->responseContent)}; - + return ($self->{url}, $result{'oslc_cm:totalCount'}{content}); } # find @@ -1511,7 +1510,7 @@ An hash representing the qualifying record. sub getDBID ($$;@) { my ($self, $table, $dbid, @fields) = @_; - + =pod =head2 get ($table, $key, @fields) @@ -1567,13 +1566,13 @@ An hash representing the qualifying record. $url .= $self->_getRecordID ($table); $url .= '-'; $url .= $dbid; - + if (@fields) { $url .= "?oslc_cm.properties="; $url .= 'dbid,' unless grep { /dbid/i } @fields; $url .= join ',', @fields; } # if - + return $self->_getRecord ($table, $url); } # getDBID @@ -1583,7 +1582,7 @@ sub getDynamicList () { sub getNext ($) { my ($self, $result) = @_; - + =pod =head2 getNext ($) @@ -1619,33 +1618,33 @@ Hash of name/value pairs for the @fields specified to find. =for html =cut - + return unless $self->{url}; - + my $url = $self->{url}; $self->_callREST ('get', $url); - + return if $self->error; # Now parse the results my %result = %{XMLin ($self->{rest}->responseContent)}; - + # Get the next link undef $self->{url}; - + if (ref $result{link} eq 'ARRAY') { foreach (@{$result{link}}) { if ($$_{rel} eq 'next') { ($self->{url}) = ($$_{href} =~ /^http.*$self->{server}(.*)/); - + last; } # if } # foreach } # if - + my %record; - + if (ref $result{entry}{content}{$self->{table}} eq 'HASH') { %record = $self->_parseFields ($self->{table}, %{$result{entry}{content}{$self->{table}}}); } elsif (ref $result{entry} eq 'HASH') { @@ -1653,18 +1652,18 @@ Hash of name/value pairs for the @fields specified to find. %record = $self->_getRecordURL ($self->{table}, $result{entry}{id}, @{$self->{fields}}); } # if } # if - + # Get dbid if ($result{entry}{link}{href} =~ /-(\d+)$/) { $record{dbid} = $1; } # if - + return %record; } # getNext sub key ($$) { my ($self, $table, $dbid) = @_; - + =pod =head2 key ($$) @@ -1710,7 +1709,7 @@ Returns: sub modify ($$$$;@) { my ($self, $table, $key, $action, $values, @ordering) = @_; - + =pod =head2 modify ($table, $key, $action, $values, @ordering) @@ -1778,36 +1777,36 @@ Error message (if any) my %values = %$values; my $xml = _startXML $table; - + $action ||= 'Modify'; - + my $query = $self->_getInternalID ($table, $key); - + # Remove host portion $query =~ s/^http.*$self->{server}//; - + # Add on action $query .= "?rcm.action=$action"; - + # First process all fields in the @ordering, if specified $xml .= $self->_setFieldValue ($table, $_, $values{$_}) foreach (@ordering); - + foreach my $field (keys %values) { next if InArray $field, @ordering; - + $xml .= $self->_setFieldValue ($table, $field, $values{$field}); } # foreach - + $xml .= ""; $self->_callREST ('put', $query, $xml); - + return $self->errmsg; } # modify sub modifyDBID ($$$$;@) { my ($self, $table, $dbid, $action, $values, @ordering) = @_; - + =pod =head2 modifyDBID ($table, $dbid, $action, %update) @@ -1875,36 +1874,36 @@ Error message (if any) my %values = %$values; my $xml = _startXML $table; - + $action ||= 'Modify'; - + my $query = "$self->{uri}/record/"; $query .= $self->_getRecordID ($table); $query .= '-'; $query .= $dbid; - + # Add on action $query .= "?rcm.action=$action"; - + # First process all fields in the @ordering, if specified $xml .= $self->_setFieldValue ($table, $_, $values{$_}) foreach (@ordering); - + foreach my $field (keys %values) { next if InArray $field, @ordering; - + $xml .= $self->_setFieldValue ($table, $field, $values{$field}); } # foreach - + $xml .= ""; $self->_callREST ('put', $query, $xml); - + return $self->errmsg; } # modifyDBID sub new (;%) { my ($class, $self) = @_; - + =pod =head2 new (%parms) @@ -1941,7 +1940,7 @@ Returns: =cut $self->{server} ||= $Clearquest::OPTS{CQ_SERVER}; - + $$self{base_url} = "$self->{server}/cqweb/oslc", $$self{uri} = "/cqweb/oslc/repo/$self->{dbset}/db/$self->{database}", $$self{login} = { @@ -1950,19 +1949,19 @@ Returns: Authorization => 'Basic ' . encode_base64 "$self->{username}:$self->{password}", }; - + bless $self, $class; - + # We create this UserAgent and Cookie Jar so we can set cookies to be # remembered and passed back and forth automatically. By doing this we re-use # the JSESSIONID cookie we allows us to reuse our login and to dispose of the # login session properly when we are destroyed. my $userAgent = LWP::UserAgent->new; - + # Set the cookie jar to use in-memory cookie management, cookies can be # persisted to disk, see HTTP::Cookies for more info. $userAgent->cookie_jar (HTTP::Cookies->new); - + $self->{rest} = REST::Client->new ( host => $self->{server}, timeout => 15, @@ -1975,7 +1974,7 @@ Returns: sub records () { my ($self) = @_; - + =pod =head2 records () @@ -2011,27 +2010,27 @@ Hash of records and their record numbers =cut return if %RECORDS; - + my $url = "$self->{uri}/record-type/"; $self->_callREST ('get', $url); - + unless ($self->error) { my %result = %{XMLin ($self->{rest}->responseContent)}; foreach my $uri (keys %{$result{entry}}) { my ($recordID) = ($uri =~ /\/(\d+)/); - + $RECORDS{$result{entry}{$uri}{title}} = $recordID; } # foreach } # unless - + return %RECORDS; } # records sub response () { my ($self) = @_; - + =pod =head2 response () diff --git a/lib/Clearquest/Server.pm b/lib/Clearquest/Server.pm index 6a514c5..d016e4e 100644 --- a/lib/Clearquest/Server.pm +++ b/lib/Clearquest/Server.pm @@ -87,14 +87,14 @@ sub new (;%) { my ($class, %parms) = @_; my $self; - + $parms{CQ_DATABASE} ||= $Clearquest::OPTS{CQ_DATABASE}; $parms{CQ_USERNAME} ||= $Clearquest::OPTS{CQ_USERNAME}; $parms{CQ_PASSWORD} ||= $Clearquest::OPTS{CQ_PASSWORD}; $parms{CQ_DBSET} ||= $Clearquest::OPTS{CQ_DBSET}; $parms{CQ_SERVER} ||= $Clearquest::OPTS{CQ_SERVER}; $parms{CQ_PORT} ||= $Clearquest::OPTS{CQ_PORT}; - + $parms{CQ_MULTITHREADED} = $Clearquest::OPTS{CQ_MULTITHREADED} unless defined $parms{CQ_MULTITHREADED}; @@ -110,7 +110,7 @@ sub new (;%) { $self->{port} = $parms{CQ_PORT}; $self->{module} = $parms{CQ_MODULE}; $self->{multithreaded} = $parms{CQ_MULTITHREADED}; - + return bless $self, $class; } # new @@ -120,7 +120,7 @@ sub _tag ($) { my $tag = YMDHMS; $tag .= ' '; $tag .= $self->{pid} ? '[' . abs ($self->{pid}) . '] ' : ''; - + return "$tag$msg"; } # _tag @@ -128,32 +128,32 @@ sub _verbose ($) { my ($self, $msg) = @_; verbose $self->_tag ($msg); - + return; } # _verbose sub _debug ($) { my ($self, $msg) = @_; - + debug $self->_tag ($msg); - + return; } # _debug sub _log ($) { my ($self, $msg) = @_; - + display $self->_tag ($msg); - + return; } # log sub _funeral () { debug "Entered _funeral"; - + while (my $childpid = waitpid (-1, WNOHANG) > 0) { my $status = $?; - + if ($childpid != -1) { local $SIG{CHLD} = \&_funeral; @@ -164,21 +164,21 @@ sub _funeral () { if $status; } # if } # while - + return; } # _funeral sub _endServer () { display "Clearquest::Server V$VERSION shutdown at " . localtime; - + # Kill process group kill 'TERM', -$$; - + # Wait for all children to die while (wait != -1) { # do nothing } # while - + # Now that we are alone, we can simply exit exit; } # _endServer @@ -186,39 +186,39 @@ sub _endServer () { sub _restartServer () { # Not sure what to do on a restart server display 'Entered _restartServer'; - + return; } # _restartServer sub _printStatus ($) { my ($self, $client) = @_; - + my $status = $self->{clearquest}->error; - + $status ||= 0; - + $self->_debug ("Printing status: " . __PACKAGE__ . " Status: $status"); - + print $client __PACKAGE__ . " Status: $status\n"; - + $self->_debug ("After print"); - + return; } # printStatus sub _connectToClearquest ($$$$) { my ($self, $database, $username, $password, $dbset) = @_; - + my %parms; - + $parms{CQ_DATABASE} = $database; $parms{CQ_USERNAME} = $username; $parms{CQ_PASSWORD} = $password; $parms{CQ_DBSET} = $dbset; - + # The server always uses the standard Clearquest API $parms{CQ_MODULE} = 'api'; - + # Connect to Clearquest database $self->{clearquest} = Clearquest->new (%parms); @@ -227,22 +227,22 @@ sub _connectToClearquest ($$$$) { . " for $self->{clientname}"); $self->{loggedin} = $self->{clearquest}->connect; - + return $self->{loggedin}; } # _connectToClearquest sub _processCommand ($$@) { my ($self, $client, $call, @parms) = @_; - + $self->_debug ("Client wishes to execute $call"); if ($call eq 'end') { $self->_verbose ("Serviced requests from $self->{clientname}"); - + close $client; $self->disconnectFromClient; - + return 1; } elsif ($call eq 'open') { debug "connectToClearquest"; @@ -261,7 +261,7 @@ sub _processCommand ($$@) { $self->_printStatus ($client); } elsif ($call eq 'get') { my %record = $self->{clearquest}->get (@parms); - + unless ($self->{clearquest}->error) { foreach my $field (keys %record) { # TODO: Need to handle field types better... @@ -269,20 +269,20 @@ sub _processCommand ($$@) { foreach (@{$record{$field}}) { # Change \n's to s/\r\n/ /gm; - + print $client "$field\@\@$_\n"; } # foreach } else { # Change \n's to $record{$field} =~ s/\r\n/ /gm; - + print $client "$field\@\@$record{$field}\n"; } # if } # foreach } else { print $client $self->{clearquest}->errmsg . "\n"; } # unless - + $self->_printStatus ($client); } elsif ($call eq 'find') { my ($result, $nbrRecs) = $self->{clearquest}->find (@parms); @@ -292,14 +292,14 @@ sub _processCommand ($$@) { } else { # Store away $result so we can use it later $self->{result} = $result; - + print $client "$result\n$nbrRecs\n"; } # if $self->_printStatus ($client); } elsif ($call eq 'getnext') { my %record = $self->{clearquest}->getNext ($self->{result}); - + unless ($self->{clearquest}->error) { foreach my $field (keys %record) { # TODO: Need to handle field types better... @@ -307,37 +307,37 @@ sub _processCommand ($$@) { foreach (@{$record{$field}}) { # Change \n's to s/\r\n/ /gm; - + print $client "$field\@\@$_\n"; } # foreach } else { # Change \n's to $record{$field} =~ s/\r\n/ /gm; - + print $client "$field\@\@$record{$field}\n"; } # if } # foreach } else { print $client $self->{clearquest}->errmsg . "\n"; } # unless - + $self->_printStatus ($client); } elsif ($call eq 'getdynamiclist') { # TODO Better error handling/testing my @entry = $self->{clearquest}->getDynamicList (@parms); - + print $client "$_\n" foreach @entry; - + $self->_printStatus ($client); } elsif ($call eq 'dbsets') { # TODO Better error handling/testing print $client "$_\n" foreach ($self->{clearquest}->DBSets); - + $self->_printStatus ($client); } elsif ($call eq 'key') { # TODO Better error handling/testing print $client $self->{clearquest}->key (@parms) . "\n"; - + $self->_printStatus ($client); } elsif ($call eq 'modify' or $call eq 'modifyDBID') { my $table = shift @parms; @@ -345,29 +345,29 @@ sub _processCommand ($$@) { my $action = shift @parms; # Need to turn off strict for eval here... - my ($values, @ordering); + my ($values, @ordering); no strict; eval $parms[0]; - + $values = $VAR1; use strict; - + @ordering = @{$parms[1]} if ref $parms[1] eq 'ARRAY'; - + my $errmsg; - + if ($call eq 'modify') { $errmsg = $self->{clearquest}->modify ($table, $key, $action, $values, @ordering); } elsif ($call eq 'modifyDBID') { $errmsg = $self->{clearquest}->modifyDBID ($table, $key, $action, $values, @ordering); } # if - + print $client "$errmsg\n" if $errmsg ne ''; $self->_printStatus ($client); } elsif ($call eq 'add') { my $dbid = $self->{clearquest}->add (@parms); - + if ($self->{clearquest}->error) { print $client 'ERROR: ' . $self->{clearquest}->errmsg () . "\n"; } # if @@ -384,12 +384,12 @@ sub _processCommand ($$@) { } else { $self->{clearquest}->{errnbr} = -1; $self->{clearquest}->{errmsg} = "Unknown call $call"; - + print $client $self->{clearquest}->errmsg . "\n"; - + $self->_printStatus ($client); } # if - + return; } # _processCommand @@ -400,28 +400,28 @@ sub _serviceClient ($) { # Set autoflush for client $client->autoflush if $client; - + my $line; - + $self->_debug ("Reading request from client"); - + while ($line = <$client>) { $self->_debug ("Request read: $line"); - + if ($line) { chomp $line; chop $line if $line =~ /\r$/; } else { $self->_verbose ("Host $self->{clientname} went away!"); - + close $client; - + return; } # if if ($line =~ /^shutdown/i) { if ($self->{server}) { $self->_verbose ("$self->{clientname} requested to shutdown the server"); - + print $client __PACKAGE__ . " Status: 0\n"; } # if @@ -430,26 +430,26 @@ sub _serviceClient ($) { exit 1; } # if - + # Parse command line my ($call, @parms); - + if ($line =~ /^\s*(\S+)\s+(.*)/) { $call = lc $1; - + no strict; eval $2; - + @parms = @$VAR1; use strict; - + my $i = 0; - + foreach (@parms) { if (/^\$VAR1/) { no strict; eval; - + $parms[$i++] = $VAR1; use strict; } else { @@ -461,7 +461,7 @@ sub _serviceClient ($) { @parms = (); } else { my $errmsg = "Garbled command line: '$line'"; - + if ($self->{clearquest}) { $self->{clearquest}->{errnbr} = -1; $self->{clearquest}->{errmsg} = $errmsg; @@ -470,17 +470,17 @@ sub _serviceClient ($) { } else { print "$errmsg\n"; } # if - + $self->_printStatus ($client); - + return; } # if - + $self->_debug ("Processing command $call @parms"); - + last if $self->_processCommand ($client, $call, @parms); } # while - + return; } # _serviceClient @@ -488,9 +488,9 @@ sub multithreaded (;$) { my ($self, $newValue) = @_; my $oldValue = $self->{multithreaded}; - + $self->{multithreaded} = $newValue if $newValue; - + return $oldValue } # multithreaded @@ -502,27 +502,27 @@ sub disconnectFromClient () { $self->_verbose ("Disconnected from client $self->{clientname}") if $self->{clientname}; - + undef $self->{clientname}; - + return; } # disconnectFromClient sub DESTROY () { my ($self) = @_; - - $self->disconnectFromClient; - + + $self->disconnectFromClient; + if ($self->{socket}) { - close $self->{socket}; - - undef $self->{socket}; - } # if + close $self->{socket}; + + undef $self->{socket}; + } # if } # DESTROY - + sub startServer () { my ($self) = @_; - + # Create new socket to communicate to clients with $self->{socket} = IO::Socket::INET->new ( Proto => 'tcp', @@ -536,9 +536,9 @@ sub startServer () { # Announce ourselves $self->_log (__PACKAGE__ . " V$VERSION accepting clients at " . localtime); - + $SIG{HUP} = \&_endServer; - + # Now wait for an incoming request my $client; @@ -551,10 +551,10 @@ sub startServer () { } else { error "Accept called failed (Error: $?) - $!", 1; } # if - } # if - + } # if + my $hostinfo = gethostbyaddr $client->peeraddr; - + $self->{clientname} = $hostinfo ? $hostinfo->name : $client->peerhost; $self->_verbose ("$self->{clientname} is requesting service"); @@ -568,7 +568,7 @@ sub startServer () { error "Can't fork: $!" unless defined ($childpid = fork); - + if ($childpid) { $self->{pid} = $$; @@ -585,18 +585,18 @@ sub startServer () { } else { # In child process - ServiceClient $self->{pid} = $$; - + # Now exec the caller but set STDIN to be the socket. Also pass # -serviceClient to the caller which will need to handle that and call # _serviceClient. $self->_debug ("Client: $client"); open STDIN, '+<&', $client or croak "Unable to dup client"; - + my $cmd = "cqperl \"$FindBin::Bin/$FindBin::Script -serviceClient=$self->{clientname} -verbose -debug"; - + $self->_debug ("Execing: $cmd"); - + exec 'cqperl', "\"$FindBin::Bin/$FindBin::Script\"", "-serviceClient=$self->{clientname}", '-verbose', '-debug' or croak "Unable to exec $cmd"; } # if @@ -604,7 +604,7 @@ sub startServer () { $self->_serviceClient ($client); } # if } # while - + # On Windows we can't catch SIGCHLD so we need to loop around. Ugly! goto LOOP if $^O =~ /win/i; } # startServer diff --git a/lib/Machines.pm b/lib/Machines.pm new file mode 100755 index 0000000..76c1dfb --- /dev/null +++ b/lib/Machines.pm @@ -0,0 +1,357 @@ +=pod + +=head1 NAME $RCSfile: Machines.pm,v $ + +Object oriented interface to list of managed machines + +=head1 VERSION + +=over + +=item Author: + +Andrew DeFaria + +=item Revision: + +$Revision: 1.0 $ + +=item Created: + +Thu, Jul 12, 2018 5:11:44 PM + +=item Modified: + +$Date: $ + +=back + +=head1 SYNOPSIS + +Perl module to specify a list of managed machines for rexec.pl + + $machines = Machines->new (filename => "/opt/clearscm/data/machines"); + + my @machines = $machines->all; + + my @linux_machines = $machines->select(condition => 'OS = "linux"'); + +=head1 DESCRIPTION + +Machines is an OO interface to a list of managed machines. By default it parses +a file that contains machine names and other identifying information. + +=head1 ROUTINES + +The following routines are exported: + +=cut + +package Machines; + +use strict; +use warnings; + +use base 'Exporter'; + +use Carp; + +sub _parseFile() { + my ($self) = @_; + + my %machines; + + # Simple parse for now + open my $machineFile, '<', $self->{filename} + or croak "Unable to open $self->{filename} - $!"; + + while (<$machineFile>) { + chomp; + + next if /^#/; # Skip comments + + my ($name, $model, $os, $ccver, $owner, $usage) = split /:/; + + my %machineInfo = ( + model => $model, + os => $os, + ccver => $ccver, + owner => $owner, + usage => $usage, + ); + + $machines{$name} = \%machineInfo; + } # while + + close $machineFile; + + return \%machines; +} # _parseFile + +sub new(;%){ + my ($class, %parms) = @_; + +=pod + +=head2 new () + +Construct a new Machines object. The following OO style arguments are +supported: + +Parameters: + +=for html
+ +=over + +=item filename: + +Filename to parse + +=item path: + +Path where file resides + +=back + +=for html
+ +Returns:: + +=for html
+ +=over + +=item Machines object + +=back + +=for html
+ +=cut + + $parms{filename} ||= 'machines'; + + if (! -r $parms{filename}) { + croak "Unable to read $parms{filename}"; + } # if + + my $self = bless { + filename => $parms{filename}, + }, $class; # bless + + # Parse file + $self->{machines} = $self->_parseFile; + + return $self; +} # new + +sub select(;$) { + my ($self, $condition) = @_; + +=pod + +=head3 select + +Return machines that qualify based on $condition + +Parameters: + +=for html
+ +=over + +=item $condition + +Condition to apply to machine list + +=back + +=for html
+ +Returns: + +=for html
+ +=over + +=item Array of qualifying machines + +=back + +=for html
+ +=cut + + $condition //= ''; + + if ($condition) { + croak "Not supporting conditions yet"; + } else { + return %{$self->{machines}}; + } # if +} # select + +sub GetSystem($) { + my ($self, $systemName) = @_; + + return; +} # getSystem + +sub AddSystem(%) { + my ($self, %system) = @_; + + return; +} # addSystem + +sub ChangeSystem(%){ + my ($self, %system) = @_; + + return; +} # changeSystem + +sub DeleteSystem($) { + my ($self, $systemName) = @_; + + return; +} # deleteSystem + +sub DumpSystems(;$) { + my ($self, $filename) = @_; + + $filename ||= 'machines'; + + open my $file, '>', $filename + or croak "Unable to open $filename for writing - $!"; + + # Write header + print $file <<"END"; +################################################################################ +# +# File: $filename +# Description: Dump of machines for use with rexec.pl +# Author: Andrew\@DeFaria.com +# +################################################################################ +# Column 1 Machine name +# Column 2 Alias +# Column 3 Active +# Column 4 Admin name +# Column 5 Admin email +# Column 6 OS version +# Column 7 OS Type +# Column 8 Last heard from +# Column 9 Description +END + + # Write out machine info + my @fields = qw(name alias active admin email os type lastheardfrom description); + + for my $record ($self->select) { + my %machine = %$record; + + for (@fields) { + print $file "$machine{$_}|" + } # for + + print $file "\n"; + } # for + + close $file; + + return; +} # DumpSystems + +sub ReadSystemsFile(;$) { + my ($self, $filename) = @_; + + $filename ||= 'machines'; + + open my $file, '<', $filename + or croak "Unable to open $filename - $!"; + + my @systems; + + while (<$file>) { + chomp; + + next if /^#/; + + my ($name, $model, $osver, $ccver, $owner, $usage) = split ':'; + my %system = ( + name => $name, + model => $model, + ccver => $ccver, + admin => $owner, + os => $osver, + type => 'Unix', + description => $usage, + ); + + push @systems, \%system; + } # while + + close $file; + + return @systems; +} # ReadSystemsFile + +1; + +=pod + +=head2 CONFIGURATION AND ENVIRONMENT + +DEBUG: If set then $debug in this module is set. + +VERBOSE: If set then $verbose in this module is set. + +=head2 DEPENDENCIES + +=head3 Perl Modules + +L + +L + +=head3 ClearSCM Perl Modules + +=for html

DateUtils

+ +=for html

Display

+ +=for html

Mail

+ +=for html

OSDep

+ +=for html

Utils

+ +=head2 INCOMPATABILITIES + +None yet... + +=head2 BUGS AND LIMITATIONS + +There are no known bugs in this module. + +Please report problems to Andrew DeFaria . + +=head2 LICENSE AND COPYRIGHT + +This Perl Module is freely available; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This Perl Module is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License (L) for more +details. + +You should have received a copy of the GNU General Public License +along with this Perl Module; if not, write to the Free Software Foundation, +Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +reserved. + +=cut diff --git a/lib/Machines/MySQL.pm b/lib/Machines/MySQL.pm new file mode 100755 index 0000000..398fd8f --- /dev/null +++ b/lib/Machines/MySQL.pm @@ -0,0 +1,242 @@ +=pod + +=head1 NAME $RCSfile: MySQL.pm,v $ + +MySQL Backend for Machines module + +=head1 VERSION + +=over + +=item Author + +Andrew DeFaria + +=item Revision + +$Revision: $ + +=item Created + +Mon, Jul 16, 2018 10:13:12 AM + +=item Modified + +$Date: $ + +=back + +=head1 SYNOPSIS + +Interfaces to a MySQL backend for machine information + +=head1 DESCRIPTION + +The rexec.pl script allows you to execute an arbitrary command on a set of +machines, however what set of machines? Primative exeuction involves just a +flat file with machine information listed in it. This module instead provides +a MySQL backend for this machine data. + +=head1 ROUTINES + +The following methods are available: + +=cut + +package Machines::MySQL; + +use strict; +use warnings; + +use Carp; +use DBI; + +use parent qw(Machines); + +our $VERSION = '$Revision: 1.0 $'; + ($VERSION) = ($VERSION =~ /\$Revision: (.*) /); + +my %MACHINEOPTS = ( + SERVER => 'localhost', + USERNAME => 'machines', + PASSWORD => 'w0rk$harder', +); + +sub _connect (;$) { + my ($self, $dbserver) = @_; + + $dbserver ||= $MACHINEOPTS{SERVER}; + + my $dbname = 'machines'; + my $dbdriver = 'mysql'; + + $self->{db} = DBI->connect ( + "DBI:$dbdriver:$dbname:$dbserver", + $MACHINEOPTS{USERNAME}, + $MACHINEOPTS{PASSWORD}, + {PrintError => 0}, + ) or croak ( + "Couldn't connect to $dbname database " + . "as $MACHINEOPTS{USERNAME}\@$MACHINEOPTS{SERVER}" + ); + + $self->{dbserver} = $dbserver; + + return; +} # _connect + +sub _checkRequiredFields ($$) { + my ($fields, $rec) = @_; + + for my $fieldname (@$fields) { + my $found = 0; + + for (keys %$rec) { + if ($fieldname eq $_) { + $found = 1; + last; + } # if + } # for + + return "$fieldname is required" + unless $found; + } # for + + return; +} # _checkRequiredFields + +# 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 ($_) + for (@values); + + return @returnValues; +} # _formatValues + +sub _formatNameValues (%) { + my ($self, %rec) = @_; + + my @nameValueStrs; + + push @nameValueStrs, "$_=" . $self->{db}->quote ($rec{$_}) + for (keys %rec); + + return @nameValueStrs; +} # _formatNameValues + +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 _getRecords ($;$) { + my ($self, $table, $condition) = @_; + + my ($err, $msg); + + my $statement = "select * from $table"; + + if ($condition) { + $condition .= ' and '; + } # if + + $condition .= 'active = "true"'; + $statement .= " where $condition"; + + my $sth = $self->{db}->prepare($statement); + + unless ($sth) { + ($err, $msg) = $self->_dberror('Unable to prepare statement', $statement); + + croak $msg; + } # if + + my $status = $sth->execute; + + ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement); + + return ($err, $msg) if $err; + + my %records; + + while (my $row = $sth->fetchrow_hashref) { + # Change undef to '' + $row->{$_} ||= '' for keys %$row; + + my $name = delete $row->{name}; + + $records{$name} = $row; + } # while + + return %records; +} # _getRecord + +sub new (;$) { + my ($class, $db) = @_; + + my $self = bless {}, $class; + + $self->_connect ($db); + + return $self; +} # new + +sub select(;$) { + my ($self, $condition) = @_; + + return $self->_getRecords('system', $condition); +} # select + +sub AddSystem (%) { + my ($self, %system) = @_; + + my @requiredFields = ( + 'name', + 'type', + ); + + my $result = _checkRequiredFields \@requiredFields, \%system; + + return -1, "AddSystem: $result" if $result; + + return $self->_addRecord ('system', %system); +} # AddSystem + +1; \ No newline at end of file diff --git a/rc/gitconfig b/rc/gitconfig index 2d54e48..6fdaf4b 100644 --- a/rc/gitconfig +++ b/rc/gitconfig @@ -44,3 +44,5 @@ [push] default = simple +[http] + sslVerify = false diff --git a/rc/system b/rc/system index bbbc234..d188ad2 100644 --- a/rc/system +++ b/rc/system @@ -2,7 +2,7 @@ ################################################################################ # # File: $RCSfile: system,v $ -# Revision: $Revision: 1.6 $ +# Revision: $Revision: 1.6 $ # Description: System specific settings # Author: Andrew@DeFaria.com # Created: Mon Aug 20 17:35:01 2001 -- 2.17.1