Posts mit dem Label Open-Source werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Open-Source werden angezeigt. Alle Posts anzeigen

Montag, 6. Januar 2014

How-To: Backup/Snapshots mehrerer Oracle Datenbanken mittels NetApp

Um Oracle Datenbanken zu sichern gibt es mehrere Wege:

Alle Technologien haben jeweils Ihre Vor- und Nachteile, auf diese möchte ich jetzt nicht eingehen, dieses Thema behandle ich in einem weiteren Blogpost in naher Zukunft.

Heute möchte ich mich auf die Sicherung mehrerer Oracle Datenbanken mittels Snapshottechnologie beziehen, welche als LUNs via ASM angebunden sind.

Beim manuellen Weg würde man die Datenbank in den Backup Modus (z.B. via sqlplus als sysdba) versetzen ("ALTER DATABASE BEGIN BACKUP"), einen Log-Switch durchführen ("ALTER SYSTEM ARCHIVE LOG CURRENT"), sich dann auf die NetApp mittels SSH aufschalten und den Snapshot anlegen lassen ("snap create VOLUME_NAME SNAPSHOT_NAME").

Dieses Verfahren mag bei einer Datenbank noch gut funktionieren, allerdings kommt man schnell in Zeitnot oder könnte einige Schritte vergessen, daher bietet es sich die Automatisierung an.

Ein weiterer Effekt, den man betrachten muss, sind mehrere, unterschiedliche Datenbanken, welche voneinander abhängig sind, d.h. die den gleichen Stand haben müssen, dort sollte man dann zwingend automatisieren.

Dieses habe ich im Rahmen eines Projekts gemacht und möchte hier das Perl-Skript kurz präsentieren, vielleicht hilft es noch anderen Leuten.

Vorab, das Perl-Skript bietet sicher noch den einen oder anderen Optimierungsspielraum, allerdings geht es im Projektgeschäft primär um das Erfüllen der Anforderungen, das "schön" machen ist dann ein nice-to-have, falls noch Zeit über ist.

Folgende Prerequisistes benötigt das Skript, welches auf dem Oracle Host ausgeführt werden soll:
  • Anpassung der Konfigurationsvariablen in den Zeilen: 9, 22, 72, 232
  • DBD::Oracle (z.B. via CPAN, Paketmanager, Oracle Instant Client oder eine volle Oracle Datenbank [das Skript ist auf diesen Fall konfiguriert, siehe Zeile "use lib xxx"])
  • DBI
  • Net::SSH::Expect (via CPAN)
  • Key-based SSH-Login auf der NetApp mit möglichst wenigen Rechten (wie z.B. hier beschrieben: http://cmdcmplt.wordpress.com/category/ssh-snapshot-filer-netapp-passwordless-roles/ Achtung: bei neueren OnTap Versionen wird als zusätzliches Recht: ssh-login benötigt: useradmin role modify snaps -a login-ssh,cli-snap*)
  • Volume-Name auf der NetApp: Diskgroup-name  + Zusatz "_vol" z.B. DATA_DB1_vol

#!/usr/bin/perl -w
# Author: Oliver Skibbe oliskibbe (at) gmail.com
# Date: 2014-01-06
# Purpose: Backup multiple Oracle ASM Databases with NetApp and snapshotting
#
#
use strict;

use lib "/u01/app/oracle/11.2.0.3/perl/lib/site_perl/5.10.0/x86_64-linux-thread-multi";

use DBI;
use Net::SSH::Expect;

# debug stuff
my $debug = 0;
my $self = undef;

# config stuff
my $auto_snaps = 5;

# databases to backup 
my %sidhash = (
    "DB1" => 
       {
       "data" => "DATA_DB1",
       "archive" => "ARCHIVE_DB1",
       "password" => 'PASSWORD'
       },
    "DB2" => 
       {
       "data" => "DATA_DB2",
       "archive" => "ARCHIVE_DB2", 
       "password" => 'PASSWORD' 
       },
    "DB3" => {
       "data" => "DATA_DB3",        
       "archive" => "ARCHIVE_DB3", 
       "password" => 'PASSWORD'
       },
); # end sidhash

### oracle connect stuff
# db handler
my $dbh = undef;
my $sth = undef;
# connect options
## ora session mode 2: sysdba
my $connecthash = { RaiseError => 0, RowCacheSize => 16, AutoCommit => 0, PrintError => 0,
              ora_session_mode => 0x0002 };
my $dsn = undef;
# user & pass
my $username = "sys";

# helper arrays
my @data_arr;
my @archive_arr;
my @row;

# backup sqls
my $b_begin_backup_sql = "alter database begin backup";
my $b_end_backup_sql = "alter database end backup";
my $b_archive_log_sql = "alter system archive log current";
my $b_control_file_trace_sql = "alter database backup controlfile to trace as '/backup/controlfiles/controlfile_PLACEHOLDER.sql' reuse";
my $b_control_file_sql = "alter database backup controlfile to '/backup/controlfiles/controlfile_PLACEHOLDER.ctl' reuse";

##################################################
#  SSH Stuff         #
##################################################

# prepare ssh object
my $ssh = Net::SSH::Expect->new (
    host => "NETAPP_HOST",
    user => 'SNAPSHOT_USER',
    raw_pty => 1
);

# just a little dumper
sub dump {
 my $message = shift || "";
 printf "%s \n", Data::Dumper::Dumper($message);
}

# begin backup, switch archive log, controlfiles
sub begin_backup { 
 foreach my $sid ( keys %sidhash ) {

  print "START: ", $sid, "\n" if $debug;
  # connect to each database
  $dsn = sprintf "DBI:Oracle:%s", $sid;
  $dbh = DBI->connect(
   $dsn, 
   $username,
   $sidhash{$sid}{'password'},
   $connecthash) || die( "begin_backup: " . $DBI::errstr . "\n" );
  
  # reuse sql for all SIDs
  (my $sid_b_control_file_trace_sql = $b_control_file_trace_sql) =~ s/PLACEHOLDER/$sid/;
  (my $sid_b_control_file_sql = $b_control_file_sql) =~ s/PLACEHOLDER/$sid/;
  
  # begin backup
  $dbh->do($b_begin_backup_sql);
  # log switch
  $dbh->do($b_archive_log_sql);
  # controlfile trace sql
  $dbh->do($sid_b_control_file_trace_sql);
  # control file
  $dbh->do($sid_b_control_file_sql);
  
  # db disconnect
  $dbh->disconnect if defined($dbh);  
 }
}

sub manage_snapshots {
 my $diskgroup = shift;
 my $snapshot = $ssh->exec(sprintf "snap list %s", $diskgroup);
 my $snapshot_name = undef;
 
 # if return value includes "no snapshot exists", create first snapshot
 if ( $snapshot =~ /No snapshots exist/ ) {
  $snapshot_name = "AUTO_${diskgroup}_1";
  print "No auto shapshot for ", $diskgroup, " found, lets create ", $snapshot_name, "\n" if $debug;
 } else {
  # get all elements of interest
  my @snapshots = $snapshot =~ /AUTO_${diskgroup}_\d+/g;
  my $snapshot_count = @snapshots;
  
  if ( $snapshot_count >= $auto_snaps ) {
   # delete oldest snapshot
   my $snapshot_delete = $ssh->exec(sprintf "snap delete %s %s", $diskgroup, $snapshots[-1]);
   # new snapshot name
   $snapshot_name = $snapshots[-1];
  } elsif ( $snapshot_count == 0 ) {
   $snapshot_name = "AUTO_${diskgroup}_1";
  } else {
   my @list;   
   foreach my $snapshot ( @snapshots ) {
    push(@list,substr($snapshot,-1));
   }
   @list = sort(@list);
   print "List: ", @list, "\n\n" if $debug;
   
   my $lo = 0;
   my $hi = $auto_snaps;
   
   my $idx = 0;
   for (my $cnt=$lo;$cnt<=$hi;$cnt++) {
    if ($cnt == $list[$idx]) {
     $idx++;
    } else {
     # free index will be used for name
     $snapshot_name = sprintf "AUTO_%s_%s", $diskgroup, $cnt;
    }
   }
  }  
 }
 print "Snapshot: ", $snapshot_name, " for diskgroup ", $diskgroup, " will be created\n";
 # create snapshot
 my $create_snapshot = $ssh->exec(sprintf "snap create %s %s", $diskgroup, $snapshot_name);
}

sub end_backup {
 my $dbh = undef;
 foreach my $sid ( keys %sidhash ) {
  print "END: ", $sid, "\n" if $debug;
  # connect to each database
  $dsn = sprintf "DBI:Oracle:%s", $sid;
  $dbh = DBI->connect(
   $dsn, 
   $username,
   $sidhash{$sid}{'password'},
   $connecthash) || die( "end_backup: " . $DBI::errstr . "\n" );
  $dbh->{TraceLevel} = 0;
  # end backup
  $dbh->do($b_end_backup_sql);
  
  # disconnect
  $dbh->disconnect if defined($dbh);
 }
}

# base sql to get groups and generate volume names, assumption: volume name is asm diskgroup name + _vol e.g. DATA_VISITOUR_1_vol
my $diskgroup_sql = 'SELECT
    d.GROUP_NUMBER, 
    g.NAME AS groupname, 
    d.NAME, 
    LOWER (d.NAME)||\'_vol\' AS volume 
   FROM 
    v$asm_diskgroup g, 
    v$asm_disk d 
   WHERE 
    d.GROUP_NUMBER = g.GROUP_NUMBER 
    AND g.NAME IN (?,?,?)';

#####################################
# Begin Backup      #
#####################################

begin_backup();

# prepare arrays for sql statement
foreach my $sid ( keys %sidhash ) {
 push(@data_arr, $sidhash{$sid}{'data'});
 push(@archive_arr, $sidhash{$sid}{'archive'});
}

# now start the ssh process
$ssh->run_ssh() or die "SSH process couldn't start: $!";
#
#$ssh->timeout(10); 
sleep(3);
# you should be logged on now. Test if you received the remote prompt:
my $counter = 0;
while ($counter <= 10) {
 if ($ssh->read_all(10) =~ />\s*\z/) {
  # break out if login prompt
  last;
 } else {
  $ssh->run_ssh();
 }
 $counter++;
}

# disable terminal translations and echo on the SSH server executing on the server the stty command:
$ssh->exec("stty raw -echo");

##################################################
# Get Diskgroups          #
##################################################
$dbh = DBI->connect(
 'DBI:Oracle:DB1', 
 $username,
 $sidhash{'DB1'}{'password'},
 $connecthash) || die( "get_diskgroup: " . $DBI::errstr . "\n" );

# parse and prepare query
$sth = $dbh->prepare($diskgroup_sql);

##############################
# DATA SNAPSHOTS    #
##############################
# execute query for data diskgroups
$sth->execute(@data_arr);

# create snapshots for each data volume on atlas-2
while (my @row = $sth->fetchrow_array()) {
 # create, delete snapshots
 manage_snapshots($row[3]);
}

# executes end backup sql
end_backup();

##############################
# ARCHIVE SNAPSHOTS   #
##############################
# log switch
$dbh->do($b_archive_log_sql);

# execute query for archive diskgroups
$sth->execute(@archive_arr);

# create snapshots for each archive volume on atlas-2
while (my @row = $sth->fetchrow_array()) {
 # create, delete snapshots
 manage_snapshots($row[3]);
}

END {
 # closes the ssh connection
 $ssh->close() if defined($ssh);
 # closes db handler
    $dbh->disconnect if defined($dbh);
}
# EOF


Folgende Schritte führt das Skript aus:
  • Über einen Hash (Zeile: 22) werden 1-n Datenbanken in den Backup-Modus versetzt, ein Log-Switch wird durchgeführt, ein Trace Controlfile und das Plain SQL Controlfile werden exportiert.
  • SSH Login (Zugangsdaten Zeile 72,73) zur NetApp
  • Hole alle Diskgroups, aus den konfigurierten Datenbanken (Quell-DB-Name ist dort hardcodiert, siehe Zeilen 232 und 234)
  • Starte für jede DATA Diskgroup (es wird angenommen, dass die NetApp Volumes im Format: DISKGROUP_vol benamt wurden) die Snapshoterstellung (Format: VOLNAME_1-X (siehe dazu Zeile 19, maximale Anzahl von Snapshots), der älteste Snapshot wird jeweils vorher gelöscht, wenn die maximale, konfigurierte Anzahl erreicht wurde
  • Beende Backup-Modus
  • Weiterer Log-Switch
  • Snapshoterstellung für die Archive Log Volumes im gleichen Format wie bei den Data Diskgroups
  • Ende..

Kleiner Hinweis: In der Schleifenlogik für die Erstellung  der Snapshotnamen gibt es noch einen kleinen Bug, bei der Generierung des Namens für den zweiten Snapshot, hat aber keine Einschränken auf den Produktiveinsatz.

Und hier natürlich noch der Download-Link: https://dl.dropboxusercontent.com/u/9482545/smo_backup.pl


Bei Fragen bitte wie immer melden.

Montag, 15. Juli 2013

Meine entwickelten Nagios Plugins

Da ich sehr viel im Bereich Monitoring, speziell Nagios und Icinga, unterwegs bin, habe ich natürlich mit der Zeit auch einige Plugins selbst geschrieben oder auch andere Plugins nach meinen Vorstellungen, Anforderungen überarbeitet bzw. Funktionen nachgerüstet.

Meine eigenen Plugins sind meist in PHP oder Bash geschrieben, die überarbeiteten Plugins sind in PHP, Perl, Powershell, Python etc. geschrieben.

Die meisten davon gibt es zum freien Download bei http://exchange.nagios.org

Diese Plugins will ich hier kurz vorstellen:

Eigenentwicklungen:
  • check_beronet (geschrieben in PHP)
    Überprüft die hervorragenden VoIP Gateways (ISDN,FXS/FXO,PRI zu SIP) der deutschen Marke Beronet (http://www.beronet.com)
    • Fehler der Ports (inkl. Performance Daten)
    • Status des Links
    • Gesamt Anrufe + durchschnittliche Zeit pro Gespräch (inkl. Performance Daten)
    •  Momentane Anrufe (inkl. Performance Daten)
    • Auslastung der Box/Karte (inkl. Performance Daten)
    • Systeminformationen: Revision, Firmware, Seriennummer
    • Uptime
  • check_all4xxx (geschrieben in PHP)
    Überprüft Sensorboxen [ALL4000/ALL4500] der deutschen Marke Allnet auf Schwellenwerte (z.B. Temperatur, Luftfeuchtigkeit, Rauchsensor, Lichtsensor, ...) und gibt diese inkl. Performance Daten zurück.
    Die Sensoren werden über Zahlenwerte ausgewählt.
  • check_otrs_tickets.php (geschrieben in PHP)
    Eines meiner weiteren Lieblingsprodukte, OTRS das führende Open-Source Ticket-Request System.
    Dieses Plugin überprüft eine konfigurierte OTRS Datenbank auf eine Menge an Tickets. Damit es flexibel ist, können die IDs der entsprechenden States und Queues über einen Parameter abgefragt und konfiguriert werden. Liefert ebenfalls Performance Daten zurück.
  • check_file_count.sh (geschrieben in BASH)
    Überprüft einen Pfad auf eine konfigurierbare Menge an Dateien und gibt die älteste Datei zurück (im Anwendungsfall gibt es einen Spooler, der manchmal nicht aufräumt..), inkl. Performance Daten.
  • check_file_count.ps1 (geschrieben in Powershell)
    Das selbe Plugin, nur für Windows. Überprüft einen Pfad auf eine konfigurierbare Menge an Dateien, inkl. Performance Daten.
Überarbeitete/übernommene Plugins:
  • check_printer (geschrieben in PHP) - hinzugefügt/überarbeitet: Performance Daten, zusätzliche Drucker, snmp v2c, snmp v3 vorbereitet, Bug-Fixes
    Dieses Plugin fragt die gängigsten Drucker mit SNMP 1/v2c und später mit SNMP Version 3 ab
    • counter - z.B. Menge an bisherig gedruckten Seiten, inkl. Performance Daten
    • toner/Ink  - liefert den Füllstand des Toners oder der Tinte zurück, inkl. Performance Daten
    • paper -  liefert den Füllstand der Papiertrays zurück (jedes tray kann separat abgefragt werden!), inkl. Performance Daten
    • hardware - gibt Hardware-Daten (z.B. CPU) zurück
    • parts - gibt den Zustand bestimmter Bauteile zurück
    • alerts - gibt Ereignisse zurück, unwichtige Ereignisse werden soweit ignoriert
    • accounting -  gibt den Status einer vorher konfigurierten Kostenstelle zurück
  • check_ups (geschrieben in PERL) -  hinzugefügt/überarbeitet check_APC: Schwellenwerte, Verbleibende Zeit auf Batterie, Ausgabe, Bug-Fixes
    Dieses Plugin fragt die gängigen USVen ab
    • Globaler Status
    • UPS/USV Type
    • Batteriekapazität
    • Leistung in %
    • Temperatur
    • Leistung in Wh
    • Verbleibende Zeit auf Batterie
    • Seriennummer, Herstellungsdatum (informativ..), Firmware-Version

Das sind natürlich nicht alle, aber schon mal eine gewisse Auswahl, vielleicht hilft es dem einen oder anderen mal.

Falls jemand Interesse, Ideen für neue Plugins oder Änderungen an bestehenden Plugins hat, bitte einfach kurz melden.