Diff -- /var/www/vhosts/3dshawn.com/site1/_schema_monitor.cgi
Diff
/var/www/vhosts/3dshawn.com/site1/_schema_monitor.cgi
added on local at 2026-07-10 18:57:33
Added
+521
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to bafb8a7507c6
to bafb8a7507c6
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.| 1 | #!/usr/bin/perl | |
| 2 | ||
| 3 | ###################################################################################################################### | |
| 4 | ###################################################################################################################### | |
| 5 | # _____ __ __ ___ _ __ | |
| 6 | # / ___/ __ __ _____ / /_ ___ ____ ___ / |/ /____ ____ (_)/ /_ ____ _____ | |
| 7 | # \__ \ / / / // ___// __// _ \ / __ `__ \ / /|_/ // __ \ / __ \ / // __// __ \ / ___/ | |
| 8 | # ___/ // /_/ /(__ )/ /_ / __// / / / / / / / / // /_/ // / / // // /_ / /_/ // / | |
| 9 | # /____/ \__, //____/ \__/ \___//_/ /_/ /_/ /_/ /_/ \____//_/ /_//_/ \__/ \____//_/ | |
| 10 | # /____/ | |
| 11 | # | |
| 12 | # Program:(V2.1) Developed By:(Shawn Pickering) Email:(shawn@shawnpickering.com) | |
| 13 | ###################################################################################################################### | |
| 14 | ###################################################################################################################### | |
| 15 | #Start the process running | |
| 16 | # nohup ./_schema_monitor.cgi & | |
| 17 | ||
| 18 | #Kill the running process, replace process_id with the actual process id returned from the ps -ax command | |
| 19 | #ps -ax|grep _schema_monitor.cgi | |
| 20 | #kill -9 process_id | |
| 21 | ||
| 22 | #If it won't start or keeps exiting after start then run this to problem solve | |
| 23 | # cat nohup.out | |
| 24 | ||
| 25 | ######################################################################## | |
| 26 | ## Features To Finish | |
| 27 | ######################################################################## | |
| 28 | # - Schema Monitor, if you remove the database from the database list then don't say deleted on any tables, if the table is missing but the database is in the list then the table was deleted and should be reported | |
| 29 | # - Schema Monitor, if there is a - and a + and they have the same exact change then nothing changed, it might have just moved position in the two files that were diff'ed, don't record or show these | |
| 30 | # - Schema Monitor, add a feature that lets you set if the database type you are monitoring is a production, development or other type of database, pre-append this to the emails | |
| 31 | # that are sent out, add the ability to type what you want in that spot and add a feature that lets you group the reports by this spot/tag. | |
| 32 | # | |
| 33 | # - Schema Monitor, add the ability to set record_tracking on tables as well, this way we can ignore temp tables etc... if we want to | |
| 34 | # - Schema Monitor, group changes in the GUI by database, show how many changes in the DB per day etc... | |
| 35 | ||
| 36 | # - A feature to separate tables or databases with changes, maybe just a secondary view of the tables/a second page that shows things grouped by database, click to show the table changes for that day | |
| 37 | ||
| 38 | #Check for table locks or in use tables, check each table before reading or writing | |
| 39 | ||
| 40 | #In_use show how many threads are currently using this table, meaning have it locked or waiting on the table lock for this table to lock it. | |
| 41 | ||
| 42 | #Name_locked shows whenever the name for this table is locked. It is used for DROP or RENAME TABLE, so you would very rarely see this field to contain anything else than 0. | |
| 43 | ||
| 44 | # SHOW OPEN TABLES; | |
| 45 | # SHOW OPEN TABLES WHERE `Table` LIKE '%[TABLE_NAME]%' AND `Database` LIKE '[DBNAME]' AND In_use > 0; | |
| 46 | # SHOW OPEN TABLES WHERE `Database` LIKE 'SmartBOM' AND In_use=0 AND Name_locked=0; | |
| 47 | # SHOW OPEN TABLES WHERE `Database`='SmartBOM' AND `Table`='BOM_ITEM'; | |
| 48 | # SHOW OPEN TABLES WHERE In_use > 0 OR Name_locked > 0; | |
| 49 | ||
| 50 | # SHOW OPEN TABLES FROM SmartBOM; | |
| 51 | # SHOW OPEN TABLES FROM SmartBOM WHERE `Table`='BOM_ITEM'; | |
| 52 | ||
| 53 | ||
| 54 | ||
| 55 | ######################################################################## | |
| 56 | ## Modules | |
| 57 | ######################################################################## | |
| 58 | use strict; | |
| 59 | use DBI; | |
| 60 | ||
| 61 | #Load the mail module; | |
| 62 | use MODS::Mail; | |
| 63 | my $mail = new MODS::Mail; | |
| 64 | ||
| 65 | ||
| 66 | ||
| 67 | ######################################################################## | |
| 68 | ## Variables | |
| 69 | ######################################################################## | |
| 70 | # Schema Change Software MySQL database configurations | |
| 71 | #Staging------------------------ | |
| 72 | my $db_host = "98.7.189.33"; | |
| 73 | my $db_name = "SYSTEM_MONITOR"; | |
| 74 | my $db_port = "3306"; | |
| 75 | my $db_user = "root"; | |
| 76 | my $db_pass = 'mariapass1'; | |
| 77 | ||
| 78 | #Production--------------------- | |
| 79 | #my $db_host = "98.7.189.39"; | |
| 80 | #my $db_name = "SCHEMA_MONITOR"; | |
| 81 | #my $db_port = "3306"; | |
| 82 | #my $db_user = "root"; | |
| 83 | #my $db_pass = 'mariapass1'; | |
| 84 | ||
| 85 | ||
| 86 | #Number of seconds between each db dump | |
| 87 | my $pause_seconds = '60'; #300 is 5 min | |
| 88 | ||
| 89 | #Save all errors to this error file | |
| 90 | my $error_file = './.errors.dat'; | |
| 91 | ||
| 92 | ||
| 93 | ||
| 94 | ######################################################################## | |
| 95 | ## Start the process | |
| 96 | ######################################################################## | |
| 97 | while(){ | |
| 98 | #-------------------------------------------------------------------------------- | |
| 99 | #Open the database connection | |
| 100 | my $dsn = "DBI:mysql:$db_name:$db_host:$db_port"; | |
| 101 | my $dbh = DBI->connect($dsn,$db_user,"$db_pass"); | |
| 102 | ||
| 103 | #Hash of hash references, contains database names & emails to contact if changes are found | |
| 104 | my %databases = (); | |
| 105 | my %databases_tables = (); | |
| 106 | ||
| 107 | #Get a list of databases to monitor | |
| 108 | my $sql = qq~SELECT * FROM DATABASE_MONITOR_SETTINGS WHERE status='1'~; | |
| 109 | my $sth = $dbh->prepare($sql); | |
| 110 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 111 | my @databases; | |
| 112 | while(my $data = $sth->fetchrow_hashref){ | |
| 113 | push(@databases, $data); | |
| 114 | } | |
| 115 | #-------------------------------------------------------------------------------- | |
| 116 | ||
| 117 | #Loop through the databases | |
| 118 | foreach my $database(@databases){ | |
| 119 | #Open the database connection | |
| 120 | my $dsn2 = "DBI:mysql:$database->{db_name}:$database->{hostname}:$database->{port}"; | |
| 121 | my $dbh2 = DBI->connect($dsn2,$database->{username},"$database->{password}"); | |
| 122 | ||
| 123 | #Create a list of who to notify for each database change found | |
| 124 | $databases{$database->{db_name}}->{notify_emails} = $database->{notify_emails}; | |
| 125 | #Add the ignore tables, this will prevent us from logging or tracking these tables, used mostly for temporary tables that are created by software and removed after use | |
| 126 | $databases{$database->{db_name}}->{ignore_tables} = $database->{ignore_tables}; | |
| 127 | #Add the tables to track silently which means don't emails | |
| 128 | $databases{$database->{db_name}}->{track_tables_silently} = $database->{track_tables_silently}; | |
| 129 | #Save the domain name just in case there were no emails, we still need the domain to have a value to exist | |
| 130 | $databases{$database->{db_name}}->{db_name} = $database->{db_name}; | |
| 131 | ||
| 132 | if(!$dbh2){ | |
| 133 | #Set the database connection status to 0 for connection failed | |
| 134 | my $sql = qq~UPDATE DATABASE_MONITOR_SETTINGS SET connection_status='0' WHERE database_list_id='$database->{database_list_id}' LIMIT 1~; | |
| 135 | my $sth = $dbh->prepare($sql); | |
| 136 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 137 | ||
| 138 | #Log the failed connection info | |
| 139 | my $sql = qq~INSERT INTO FAILED_SCAN_LOGS SET failed_scan_logs_id=NULL, db_name='$database->{db_name}', date_time=NOW()~; | |
| 140 | my $sth = $dbh->prepare($sql); | |
| 141 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 142 | ||
| 143 | next; | |
| 144 | }else{ | |
| 145 | #Set the database connection status to 1 for connection successful | |
| 146 | my $sql = qq~UPDATE DATABASE_MONITOR_SETTINGS SET connection_status='1', last_connection=NOW() WHERE database_list_id='$database->{database_list_id}' LIMIT 1~; | |
| 147 | my $sth = $dbh->prepare($sql); | |
| 148 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 149 | } | |
| 150 | ||
| 151 | #-------------------------------------------------------------------------------- | |
| 152 | #Get a list of the tables for the database we are in | |
| 153 | my $sql = qq~SHOW TABLES~; | |
| 154 | my $sth2 = $dbh2->prepare($sql); | |
| 155 | $sth2->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 156 | my @db_tables; | |
| 157 | while(my $data = $sth2->fetchrow_hashref){ | |
| 158 | push(@db_tables, $data); | |
| 159 | } | |
| 160 | #-------------------------------------------------------------------------------- | |
| 161 | ||
| 162 | #-------------------------------------------------------------------------------- | |
| 163 | #Create a list of tables to ignore | |
| 164 | my @ignore_tables = split(/\,/, $databases{$database->{db_name}}->{ignore_tables}); | |
| 165 | #-------------------------------------------------------------------------------- | |
| 166 | ||
| 167 | #-------------------------------------------------------------------------------- | |
| 168 | #Loop through the tables getting the table designs | |
| 169 | foreach my $sf(@db_tables){ | |
| 170 | my $tablename = $sf->{"Tables_in_$database->{db_name}"}; | |
| 171 | ||
| 172 | if($tablename ~~ @ignore_tables){ | |
| 173 | #Skip this table, we are ignoring it | |
| 174 | next; | |
| 175 | }else{ | |
| 176 | #Table is ok to monitor, continue | |
| 177 | } | |
| 178 | ||
| 179 | #-------------------------------------------------------------------------------- | |
| 180 | #Verify that this table is not in use and has no read locks currently on it, if it does skip it | |
| 181 | my $sql = qq~SHOW OPEN TABLES FROM `$database->{db_name}` WHERE `Table`='$tablename'~; | |
| 182 | my $sth = $dbh->prepare($sql); | |
| 183 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 184 | my $table_status = $sth->fetchrow_hashref; | |
| 185 | ||
| 186 | if($table_status->{In_use} > 0 || $table_status->{Name_locked} > 0){ | |
| 187 | #Don't touch this table right now, save the tables status as a failed attempt | |
| 188 | my $sql = qq~INSERT INTO LOCKED_TABLE_LOGS SET locked_table_logs_id=NULL, db_name='$database->{db_name}', table_name='$tablename', in_use='$table_status->{In_use}', name_locked='$table_status->{Name_locked}', date_time=NOW()~; | |
| 189 | my $sth = $dbh->prepare($sql); | |
| 190 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 191 | ||
| 192 | #Since this table is locked lets ignore it for this scan cycle, add this database table to the ignore list for this cycle | |
| 193 | $databases{$database->{db_name}}->{ignore_tables} .= ",$tablename"; | |
| 194 | ||
| 195 | next; #Go to the next table, done with this one until the next scan | |
| 196 | }else{ | |
| 197 | #Ok to access this table, allow the program scan this DB table | |
| 198 | } | |
| 199 | #-------------------------------------------------------------------------------- | |
| 200 | ||
| 201 | #Save the database_name.table_name combo, needed for deleting diff files for tables that were deleted | |
| 202 | $databases_tables{"$database->{db_name}.$tablename"} = 1; | |
| 203 | ||
| 204 | #-------------------------------------------------------------------------------- | |
| 205 | #Get the table design | |
| 206 | my $sql = qq~SHOW CREATE TABLE $tablename~; | |
| 207 | my $sth2 = $dbh2->prepare($sql); | |
| 208 | $sth2->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 209 | my @table_info; | |
| 210 | while(my $data = $sth2->fetchrow_hashref){ | |
| 211 | push(@table_info, $data); | |
| 212 | } | |
| 213 | ||
| 214 | #See if there is a primary key, don't do row counts on DB tables that don't have a primary key due to freezing issues from long COUNT() times | |
| 215 | my $table_info = "@table_info"; | |
| 216 | my $primary_key = 1 if $table_info =~ m/PRIMARY KEY/i; | |
| 217 | #-------------------------------------------------------------------------------- | |
| 218 | ||
| 219 | #-------------------------------------------------------------------------------- | |
| 220 | #If it says to minitor the number of rows for this table then track it | |
| 221 | if($database->{track_row_count} && $primary_key){ | |
| 222 | #Get number of records currently in each table | |
| 223 | my $sql = qq~SELECT COUNT(*)record_count FROM $tablename~; | |
| 224 | my $sth2 = $dbh2->prepare($sql); | |
| 225 | $sth2->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 226 | my $current = $sth2->fetchrow_hashref; | |
| 227 | ||
| 228 | #Check if there is a previous record for this table | |
| 229 | my $sql = qq~SELECT table_info_id, record_count FROM TABLE_INFO WHERE db_name='$database->{db_name}' AND table_name='$tablename' LIMIT 1~; | |
| 230 | my $sth = $dbh->prepare($sql); | |
| 231 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 232 | my $previous = $sth->fetchrow_hashref; | |
| 233 | ||
| 234 | if(defined($previous->{record_count})){ | |
| 235 | #Existing record found, update info | |
| 236 | my $sql = qq~UPDATE TABLE_INFO SET record_count='$current->{record_count}', date_time=NOW() WHERE table_info_id='$previous->{table_info_id}' LIMIT 1~; | |
| 237 | my $sth = $dbh->prepare($sql); | |
| 238 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 239 | ||
| 240 | #Compare for changes | |
| 241 | my $record_count_change = ($current->{record_count} - $previous->{record_count}); | |
| 242 | ||
| 243 | if($record_count_change ne '0'){ | |
| 244 | #Changed, save changes | |
| 245 | my $sql = qq~INSERT INTO TABLE_DATA_CHANGES SET table_data_changes_id=NULL, db_name='$database->{db_name}', table_name='$tablename', record_count='$current->{record_count}', record_count_change='$record_count_change', date_time=NOW()~; | |
| 246 | my $sth = $dbh->prepare($sql); | |
| 247 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 248 | }else{ | |
| 249 | #Nothing changed, do nothing | |
| 250 | } | |
| 251 | ||
| 252 | }else{ | |
| 253 | #New record found, insert info | |
| 254 | my $sql = qq~INSERT INTO TABLE_INFO SET table_info_id=NULL, record_count='$current->{record_count}', db_name='$database->{db_name}', table_name='$tablename', date_time=NOW()~; | |
| 255 | my $sth = $dbh->prepare($sql); | |
| 256 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 257 | } | |
| 258 | } | |
| 259 | #-------------------------------------------------------------------------------- | |
| 260 | ||
| 261 | #-------------------------------------------------------------------------------- | |
| 262 | #Loop through the table rows and clean them up | |
| 263 | my $cleaned_data; | |
| 264 | foreach my $tirow(@table_info){ | |
| 265 | my $skip_line=0; | |
| 266 | foreach my $row(keys %$tirow){ | |
| 267 | $skip_line++; | |
| 268 | next if $skip_line == 1; #Skip the first line only | |
| 269 | ||
| 270 | next if $tirow->{$row} =~ m/^(\-\-|\/\*)/; #Remove lines with either kind of comments -- and /* | |
| 271 | next if $tirow->{$row} !~ m/[\w\d]/i; #Remove any blank lines | |
| 272 | $tirow->{$row} =~ s/AUTO_INCREMENT=(\d+)/AUTO_INCREMENT=0/i; #Set the auto increment number to 0 so we don't monitor the number changes | |
| 273 | $cleaned_data .= $tirow->{$row}; #Save the good clean lines only | |
| 274 | } | |
| 275 | } | |
| 276 | #-------------------------------------------------------------------------------- | |
| 277 | ||
| 278 | #If there is a current table dump then move it to .prev_dump for comparing previous dump to the the new current dump | |
| 279 | `mv ./SCHEMAS/$database->{db_name}.$tablename.curr_dump ./SCHEMAS/$database->{db_name}.$tablename.prev_dump` if -e "./SCHEMAS/$database->{db_name}.$tablename.curr_dump"; | |
| 280 | ||
| 281 | #-------------------------------------------------------------------------------- | |
| 282 | #Save the database table info | |
| 283 | open(W, ">./SCHEMAS/$database->{db_name}.$tablename.curr_dump"); | |
| 284 | print W $cleaned_data . ";"; | |
| 285 | close W; | |
| 286 | #-------------------------------------------------------------------------------- | |
| 287 | } | |
| 288 | #-------------------------------------------------------------------------------- | |
| 289 | ||
| 290 | #Close the database connection | |
| 291 | $dbh2->disconnect; | |
| 292 | } | |
| 293 | ||
| 294 | ||
| 295 | #-------------------------------------------------------------------------------- | |
| 296 | #Loop through all of the tables, run a diff to find out if there were any changes | |
| 297 | opendir(DIR, "./SCHEMAS/"); | |
| 298 | my @files = readdir(DIR); | |
| 299 | close DIR; | |
| 300 | ||
| 301 | foreach my $file(@files){ | |
| 302 | #Get the DB name and table name | |
| 303 | my($file_db_name, $file_table_name, $dump_type) = split(/\./, $file); | |
| 304 | ||
| 305 | next if $dump_type eq 'prev_dump'; #Skip .prev_dump files so we don't do this twice for every database table | |
| 306 | ||
| 307 | #-------------------------------------------------------------------------------- | |
| 308 | #Create a list of tables to ignore | |
| 309 | my @ignore_tables = split(/\,/, $databases{$file_db_name}->{ignore_tables}); | |
| 310 | ||
| 311 | if($file_table_name ~~ @ignore_tables){ | |
| 312 | #Skip this table, we are ignoring it | |
| 313 | next; | |
| 314 | }else{ | |
| 315 | #Table is ok to monitor, continue | |
| 316 | } | |
| 317 | #-------------------------------------------------------------------------------- | |
| 318 | ||
| 319 | if(!$databases_tables{"$file_db_name.$file_table_name"}){ | |
| 320 | next if $file =~ m/^\./; #Skip directories | |
| 321 | ||
| 322 | #Get the last schema before deleting | |
| 323 | open(R, "./SCHEMAS/$file_db_name\.$file_table_name\.curr_dump"); | |
| 324 | my @data = <R>; | |
| 325 | close R; | |
| 326 | ||
| 327 | my $last_schema = "@data"; | |
| 328 | $last_schema = quotemeta("$last_schema"); | |
| 329 | ||
| 330 | #If this database name was deleted from our list then delete these files | |
| 331 | unlink("./SCHEMAS/$file_db_name\.$file_table_name\.curr_dump"); | |
| 332 | unlink("./SCHEMAS/$file_db_name\.$file_table_name\.prev_dump"); | |
| 333 | ||
| 334 | #-------------------------------------------------------------------------------- | |
| 335 | #Someone deleted this database table, save to the system and notify user by email | |
| 336 | #Save the changes found to the database | |
| 337 | my $sql = qq~INSERT INTO SCHEMA_CHANGE SET schema_change_id=NULL, database_name='$file_db_name', table_name='$file_table_name', changes='--table deleted--', table_schema='$last_schema', change_datetime=NOW()~; | |
| 338 | my $sth = $dbh->prepare($sql); | |
| 339 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 340 | ||
| 341 | ######################################################################################################################### | |
| 342 | # Send out emails alerting users of the changes found if there were emails to notify | |
| 343 | my @silent_tables = split(/\,/, $databases{$file_db_name}->{track_tables_silently}); | |
| 344 | ||
| 345 | if($file_table_name ~~ @silent_tables){ | |
| 346 | #Don't send | |
| 347 | }else{ | |
| 348 | #Send | |
| 349 | if($databases{$file_db_name}->{notify_emails}){ | |
| 350 | my $ToMail = $databases{$file_db_name}->{notify_emails}; | |
| 351 | my $FromMail = 'CBOPROGAAA@charter.com'; | |
| 352 | my $Subject = qq~SCHEMA MONITOR - DB: $file_db_name Table: $file_table_name~; | |
| 353 | my $Body = qq~<b>DB SCHEMA CHANGE DETECTED</b><br><br><b>Database Name:</b> $file_db_name<br><b>Table Name:</b> $file_table_name<br><b>Changes Detected:</b><br>------------------------------------------------------------------------------------------------------<br>--table deleted--<br>------------------------------------------------------------------------------------------------------<br><br><br><br><br><br>~; | |
| 354 | my $Attachement = 'NA'; #If no attachment NA | |
| 355 | my $AttachementName = 'NA'; #If no attachment NA else give it a name good for the user | |
| 356 | my $AttachmentType = 'NA'; #If no attachment NA else give proper mime type example is png image | |
| 357 | ||
| 358 | my $SaveGridState = $mail->send_system_message($ToMail,$Subject,$Body,$FromMail,$Attachement,$AttachementName,$AttachmentType); | |
| 359 | } | |
| 360 | } | |
| 361 | ######################################################################################################################### | |
| 362 | #-------------------------------------------------------------------------------- | |
| 363 | ||
| 364 | next; | |
| 365 | } | |
| 366 | ||
| 367 | #Run the diff | |
| 368 | my $diff_results; | |
| 369 | ||
| 370 | if(!-e "./SCHEMAS/$file_db_name\.$file_table_name\.prev_dump"){ | |
| 371 | #There was no file to compare to, save the entire new file as a change | |
| 372 | open(R, "./SCHEMAS/$file_db_name\.$file_table_name\.curr_dump"); | |
| 373 | my @data = <R>; | |
| 374 | close R; | |
| 375 | ||
| 376 | #Add this to the last line for comparing the next time through | |
| 377 | $data[-1] =~ s/\;$//; | |
| 378 | $data[-1] .= " AUTO_INCREMENT=0;"; | |
| 379 | ||
| 380 | #Write the data back to the file so we can properly run the diff next time | |
| 381 | open(W, ">./SCHEMAS/$file_db_name\.$file_table_name\.curr_dump"); | |
| 382 | print W @data; | |
| 383 | close W; | |
| 384 | ||
| 385 | foreach my $line(@data){ | |
| 386 | #Act like the diff, add a + to every line so we can see that they are all new | |
| 387 | $line = '+ ' . $line; | |
| 388 | ||
| 389 | $diff_results .= $line; | |
| 390 | } | |
| 391 | ||
| 392 | #Copy this new file over to the previous file so we can monitor new changes the next time this program runs | |
| 393 | `cp ./SCHEMAS/$file_db_name\.$file_table_name\.curr_dump ./SCHEMAS/$file_db_name\.$file_table_name\.prev_dump`; | |
| 394 | }else{ | |
| 395 | $diff_results = `diff -u ./SCHEMAS/$file_db_name\.$file_table_name\.prev_dump ./SCHEMAS/$file_db_name\.$file_table_name\.curr_dump`; | |
| 396 | } | |
| 397 | ||
| 398 | my @diff_results = split(/\n/, $diff_results); | |
| 399 | ||
| 400 | my $changes_found; | |
| 401 | foreach my $line(@diff_results){ | |
| 402 | next if $line =~ m/^[\+\-]{3}/; #Don't keep the diff filename info that is returned | |
| 403 | $changes_found .= qq~$line\n~ if $line =~ m/^[\+\-]\s+/; #Only keep the lines that show the changes | |
| 404 | } | |
| 405 | ||
| 406 | #Save the changes if any are found | |
| 407 | if($changes_found){ | |
| 408 | #Copy of the changes for for converting to HTML for the email | |
| 409 | my $html_changes_found = $changes_found; | |
| 410 | $html_changes_found =~ s/\n/<br>/g; #Convert newlines \n to break tags <br> | |
| 411 | ||
| 412 | #Escape any possible bad chars | |
| 413 | $changes_found = quotemeta("$changes_found"); | |
| 414 | ||
| 415 | #Get the current table_schema and save it to the DB | |
| 416 | open(R, "./SCHEMAS/$file_db_name\.$file_table_name\.curr_dump"); | |
| 417 | my @table_schema = <R>; | |
| 418 | close R; | |
| 419 | ||
| 420 | my $table_schema = "@table_schema"; | |
| 421 | $table_schema = quotemeta("$table_schema"); | |
| 422 | ||
| 423 | #-------------------------------------------------------------------------------- | |
| 424 | #Save the changes found to the database | |
| 425 | my $sql = qq~INSERT INTO SCHEMA_CHANGE SET schema_change_id=NULL, database_name='$file_db_name', table_name='$file_table_name', changes='$changes_found', table_schema='$table_schema', change_datetime=NOW()~; | |
| 426 | my $sth = $dbh->prepare($sql); | |
| 427 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 428 | ||
| 429 | ######################################################################################################################### | |
| 430 | # Send out emails alerting users of the changes found if there were emails to notify | |
| 431 | my @silent_tables = split(/\,/, $databases{$file_db_name}->{track_tables_silently}); | |
| 432 | ||
| 433 | if($file_table_name ~~ @silent_tables){ | |
| 434 | #Don't send | |
| 435 | }else{ | |
| 436 | #Send | |
| 437 | if($databases{$file_db_name}->{notify_emails}){ | |
| 438 | my $ToMail = $databases{$file_db_name}->{notify_emails}; | |
| 439 | my $FromMail = 'CBOPROGAAA@charter.com'; | |
| 440 | my $Subject = qq~SCHEMA MONITOR - DB: $file_db_name Table: $file_table_name~; | |
| 441 | my $Body = qq~<b>DB SCHEMA CHANGE DETECTED</b><br><br><b>Database Name:</b> $file_db_name<br><b>Table Name:</b> $file_table_name<br><b>Changes Detected:</b><br>------------------------------------------------------------------------------------------------------<br>$html_changes_found------------------------------------------------------------------------------------------------------<br><br><br><br><br><br>~; | |
| 442 | my $Attachement = 'NA'; #If no attachment NA | |
| 443 | my $AttachementName = 'NA'; #If no attachment NA else give it a name good for the user | |
| 444 | my $AttachmentType = 'NA'; #If no attachment NA else give proper mime type example is png image | |
| 445 | ||
| 446 | my $SaveGridState = $mail->send_system_message($ToMail,$Subject,$Body,$FromMail,$Attachement,$AttachementName,$AttachmentType); | |
| 447 | } | |
| 448 | } | |
| 449 | ######################################################################################################################### | |
| 450 | } | |
| 451 | } | |
| 452 | #-------------------------------------------------------------------------------- | |
| 453 | ||
| 454 | #Update the monitor setting with the last processed date and time | |
| 455 | my $sql = qq~INSERT INTO MONITOR_SETTING SET monitor_setting_id=NULL, key_name='processed_datetime', key_value='', timestamp=NOW() ON DUPLICATE KEY UPDATE key_name='processed_datetime', key_value='', timestamp=NOW()~; | |
| 456 | my $sth = $dbh->prepare($sql); | |
| 457 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 458 | ||
| 459 | #Close the database connection | |
| 460 | $dbh->disconnect; | |
| 461 | ||
| 462 | ||
| 463 | #Pause the process for this many seconds before running again | |
| 464 | sleep($pause_seconds); | |
| 465 | } | |
| 466 | ||
| 467 | sub error{ | |
| 468 | open(A, ">>$error_file"); | |
| 469 | print A $_ . "\n"; | |
| 470 | close A; | |
| 471 | } | |
| 472 | "); | |
| 473 | print A $_ . "\n"; | |
| 474 | close A; | |
| 475 | } | |
| 476 | mails to notify | |
| 477 | my @silent_tables = split(/\,/, $databases{$file_db_name}->{track_tables_silently}); | |
| 478 | ||
| 479 | if($file_table_name ~~ @silent_tables){ | |
| 480 | #Don't send | |
| 481 | }else{ | |
| 482 | #Send | |
| 483 | if($databases{$file_db_name}->{notify_emails}){ | |
| 484 | my $ToMail = $databases{$file_db_name}->{notify_emails}; | |
| 485 | my $FromMail = 'CBOPROGAAA@charter.com'; | |
| 486 | my $Subject = qq~SCHEMA MONITOR - DB: $file_db_name Table: $file_table_name~; | |
| 487 | my $Body = qq~<b>DB SCHEMA CHANGE DETECTED</b><br><br><b>Database Name:</b> $file_db_name<br><b>Table Name:</b> $file_table_name<br><b>Changes Detected:</b><br>------------------------------------------------------------------------------------------------------<br>$html_changes_found------------------------------------------------------------------------------------------------------<br><br><br><br><br><br>~; | |
| 488 | my $Attachement = 'NA'; #If no attachment NA | |
| 489 | my $AttachementName = 'NA'; #If no attachment NA else give it a name good for the user | |
| 490 | my $AttachmentType = 'NA'; #If no attachment NA else give proper mime type example is png image | |
| 491 | ||
| 492 | my $SaveGridState = $mail->send_system_message($ToMail,$Subject,$Body,$FromMail,$Attachement,$AttachementName,$AttachmentType); | |
| 493 | } | |
| 494 | } | |
| 495 | ######################################################################################################################### | |
| 496 | } | |
| 497 | } | |
| 498 | #-------------------------------------------------------------------------------- | |
| 499 | ||
| 500 | #Update the monitor setting with the last processed date and time | |
| 501 | my $sql = qq~INSERT INTO MONITOR_SETTING SET monitor_setting_id=NULL, key_name='processed_datetime', key_value='', timestamp=NOW() ON DUPLICATE KEY UPDATE key_name='processed_datetime', key_value='', timestamp=NOW()~; | |
| 502 | print localtime() . qq~ $sql\n~; | |
| 503 | my $sth = $dbh->prepare($sql); | |
| 504 | $sth->execute || error("ERROR: line nunber:" . __LINE__ . " could not execute sql:$sql"); | |
| 505 | print localtime() . qq~ QUERY EXECUTE COMPLETE ON LINE: ~ . __LINE__ . qq~\n~; | |
| 506 | # $sth->finish; | |
| 507 | ||
| 508 | #Close the database connection | |
| 509 | $dbh->disconnect; | |
| 510 | ||
| 511 | print qq~Finished loop: $loop, going to sleep for $pause_seconds seconds\n~; | |
| 512 | ||
| 513 | #Pause the process for this many seconds before running again | |
| 514 | sleep($pause_seconds); | |
| 515 | } | |
| 516 | ||
| 517 | sub error{ | |
| 518 | open(A, ">>$error_file"); | |
| 519 | print A $_ . "\n"; | |
| 520 | close A; | |
| 521 | } |