/
usr
/
lib
/
raider
/
Raider
/
File Upload :
llllll
Current File: //usr/lib/raider/Raider/Base.pm
use strict; use warnings; package Raider::Base; use Raider::Devices; use Raider::Jobs; use Raider::Info; use Raider::Notification::API; use File::Find (); use Client::RaidAlarm; use Cwd (); use Config; use Time::localtime; =head1 NAME Raider::Base - Common methods used throughout RAIDER. =head1 DESCRIPTION =head1 USAGE Typically, one shouldn't need to use this class directly, as it is inherited at all higher layers. However, you would use this methods new() method to make a new object, and use it from there. All other classes inherite this classes new method. =head1 METHODS =head2 new() Creates a new object. =head2 handle_opts(\%args) Tells RAIDER what work needs to be done, in response to the user inputted command line arguments. =head2 read_conf_file(\%args) Parses the conf_file, returns hashref containing config. =head2 sysFindFile(\%args) Searches in the specified search_path. =head2 prerun_tasks() Calls subs to do the following tasks: Ensure RAIDER is running with needed privileges, ensure required system commands are in the $PATH, ensure required paths exist. =head2 get_arch() Return whether the machine is 32 or 64 bit. =head2 fs_integrity_check() If the required system paths do not exist, create them. =head2 ensure_root() If not root log error, and exit. =head2 cli_path_multiple(\@args) Ensure list of commands are in the $PATH =head2 get_time() Return the current ctime. =head2 strip_whitespace(\%args) Return the trailing and leading whitespace from string. =head2 in_path(\%args) Ensures (and attempts to takes action if not) that all required system commands for typical operation are in place on the system. =head2 show_version() Prints the RAIDER version. =head2 show_help() Prints the usage help. =head2 if_running() Halts execution if lock file exists, otherwise continues the program. =head2 place_lock() Places the lock file. =head2 do_exit(\%args) Handles exiting, can either exit cleanly or uncleanly, leaving or removing the lock file before exiting. =head2 get_hostname() Returns the hostname, as returned from Sys::Hostname. If Sys::Hostname::Long is present, then the hostname it returns will be used instead. =head2 logger(\%args) This is the logging component, used throughout RAIDER. This is used to write to log_file. =head2 sys_cmd_timeout(\%) Returns true if the passed command takes longer than sys_cmd_timeout_val to run. =head2 sys_cmd_ok(\%) Returns true if the passed command doesn't timeout. This method calls sys_cmd_timeout. =head2 load_module_or_die(string) Given the perl module (such as Sys::Hostname::Long), attempt to load via requires. If this fails, send a notification to system restore API (which will send an email instead, if there is a failure posting to API) and then log and die. =head2 can_load_module(string) Given the perl module (such as Sys::Hostname::Long), return 1 if it can be loaded. If it cannot, return 0 (and make a raider log entry). =cut our %base_conf = ( raider_version => '4.0.75', # The global timeout. This means if a single run of raider runs long enough to reach this # value, raider will die. This value is in seconds. global_timeout => '600', store_path => '/var/cache/raider', jobs_path => '/var/cache/raider/jobs', etc_path => '/etc/raider', data_path => '/var/cache/raider/data', info_path => '/var/cache/raider/info', log_path => '/var/log', alert_path => '/var/cache/raider/alerts', not_root => '0', log_file => '/var/log/raider.log', perl_version => "$^V", lock_path => '/var/lock', lock_file => '/var/lock/raider.lock', blacklist_file => '/etc/raider/raider-blacklist', conf_file => '/etc/raider/raider.conf', sys_cmd_timeout_val => '480', # 8 minutes, was 2 minutes previously TR#S7Ujg4AV api_host => 'raidalarm.sysres.liquidweb.com', lpuid_file => '/usr/local/lp/etc/lp-UID', ); sub new { my ($class) = @_; # store uniq_id if available. Default to empty string to be consistent with how RaidAlarm # stores missing attributes. my $uniq_id = ''; if (-e $Raider::Base::base_conf{lpuid_file}) { open FILE, "<$Raider::Base::base_conf{lpuid_file}" or die "Unable to read [$Raider::Base::base_conf{lpuid_file}]: $!\n"; $uniq_id = do { local $/; <FILE> }; close(FILE) or die "Unable to close [$Raider::Base::base_conf{lpuid_file}]: $!\n"; chomp($uniq_id); } return bless { raidalarm => Client::RaidAlarm->new( api_host => $Raider::Base::base_conf{api_host}, raider_ver => $Raider::Base::base_conf{raider_version}, ), uniq_id => $uniq_id, }, $class; } sub handle_opts { my $self = shift; my $opts = shift; unless ($opts->{run_jobs} || $opts->{get_info} || $opts->{help} || $opts->{version} || $opts->{check_api} || $opts->{force_update_vendor_tools}) { $self->show_help(); } else { $self->place_lock(); } if ($opts->{check_api}) { my $notifObj = Raider::Notification->new(); if ($notifObj->can_raidalarm_api()) { print "Successfully reached [$Raider::Base::base_conf{api_host}]\n"; } else { warn "Error reaching [$Raider::Base::base_conf{api_host}]; check [$Raider::Base::base_conf{log_path}/raider.log] for details\n" } } if ( $opts->{run_jobs} || $opts->{force_update_vendor_tools} ) { my $devicesObj = Raider::Devices->new(); my $jobsObj = Raider::Jobs->new(); $devicesObj->get_devices($opts->{force_update_vendor_tools} //= 0); $jobsObj->run_jobs(); } if ( $opts->{get_info} ) { my $conf_file = $self->read_conf_file(); if ( $conf_file->{'get_info'} ) { my $infoObj = Raider::Info->new(); $infoObj->do_get_info(); } else { $self->logger({ cat => 'i', msg => "get-info suppressed by config file." }); } } if ( $opts->{help} ) { $self->show_help(); } if ( $opts->{version} ) { $self->show_version(); } } sub read_conf_file { my $self = shift; if ($self->{raider_conf}) { return $self->{raider_conf}; } my %config; if ( ! -e $base_conf{'conf_file'} ) { print STDERR "Conf file ($base_conf{'conf_file'}) doesn't exist!\n"; $self->do_exit(); } open my $config, '<', "$base_conf{'conf_file'}" || $self->logger({ cat => 'c', msg => "Can't open $base_conf{'conf_file'}: $!" }); while(<$config>) { chomp; s/#.*//; # ignore comments s/^\s+//; # rm leading white s/\s+$//; # rm trailing white next unless length; (my $key, my @value) = split /\s*=\s*/, $_; $config{$key} = join '=', @value; } $self->{raider_conf} = \%config; return $self->{raider_conf}; } sub sysFindFile { my $self = shift; my $opts = shift; our @cmds = (); use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; File::Find::find({wanted => \&sysFindFile_wanted}, "$opts->{search_path}"); sub sysFindFile_wanted { my ($dev,$ino,$mode,$nlink,$uid,$gid); (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && -f _ && doexec(0, 'basename','{}'); } sub doexec ($@) { my $cwd = Cwd::cwd(); my $ok = shift; my @command = @_; for my $word (@command) { $word =~ s#{}#$name#g; }; if ($ok) { my $old = select(STDOUT); $| = 1; print "@command"; select($old); return 0 unless <STDIN> =~ /^y/; } push(@cmds, "@command"); } my @files = (); for my $cmd ( @cmds ) { my $result = `$cmd`; chomp($result); push(@files, $result); }; return \@files; } sub prerun_tasks { my $self = shift; $self->ensure_root(); ## *Most* of these system commands are only required because of leftovers from the bash codebase. This would be good ## to slowly cleanup. --ssullivan 01/18/2012 my $req_cmds = [ 'col', 'basename', 'lspci', ]; $self->in_path({ cmds => $req_cmds, mode => 'fatal' }); $self->fs_integrity_check(); } sub get_arch { my $self = shift; my $arch = ( $Config{archname} =~ /x86_64/ ) ? '64bit' : '32bit'; return $arch; } sub fs_integrity_check { my $self = shift; my @required_paths = ( "$base_conf{'jobs_path'}", "$base_conf{'info_path'}", "$base_conf{'data_path'}", "$base_conf{'log_path'}", "$base_conf{'etc_path'}", "$base_conf{'alert_path'}", "$base_conf{'lock_path'}" ); for my $req_path ( @required_paths ) { if ( ! -d $req_path ) { system("mkdir -p $req_path"); } }; } sub ensure_root { my $self = shift; if ( $< != 0 ) { $base_conf{'not_root'} = 1; $self->logger({ cat => 'c', msg => "raider requires root level privileges." }); } } sub cli_path_multiple { my $self = shift; my $paths_ref = shift; my @paths = @$paths_ref; for my $cur_path (@paths) { if ( -e $cur_path ) { return 0; } }; return 1; } sub get_time { my $self = shift; my $cur_time = ctime(); return $cur_time; } sub strip_whitespace { my $self = shift; my $opts = shift; $opts->{string} =~ s/^\s+//; $opts->{string} =~ s/\s+$//; return $opts->{string}; } sub in_path { my $self = shift; my $opts = shift; my $infoObj = Raider::Info->new(); my $distro = $infoObj->get_os(); my $ret; for my $cmd( @{$opts->{cmds}} ) { ## For some reason, stderr redirection behaves differently on CentOS 5 and Squeeze.. my $redirect_cmd; if ( $distro eq 'debian' ) { $redirect_cmd = '> /dev/null'; } elsif ( $distro eq 'redhat' ) { $redirect_cmd = '&> /dev/null'; my $r_cmds = { # cmd name # pkg name 'lsb_release' => 'redhat-lsb', 'sginfo' => 'sg3_utils' }; for my $r_key ( keys %{$r_cmds} ) { system("which $r_key $redirect_cmd"); if ( $? != 0 ) { my $stderr = `yum -y install $r_cmds->{$r_key} 2>&1 1>/dev/null`; chomp($stderr); if ( $? != 0 ) { $self->logger({ cat => 'c', msg => "Error installing $r_key: $stderr" }); } else { $self->logger({ cat => 'i', msg => "Installed missing $r_key" }); } } }; } else { $self->logger({ cat => 'c', msg => "get_os() returned unknown value: $distro" }); } system("which $cmd $redirect_cmd"); if ( $? != 0 ) { if ( $opts->{mode} ) { if ( $opts->{mode} eq 'nofatal' ) { $ret = 'not_found'; } elsif ( $opts->{mode} eq 'fatal' ) { $self->logger({ cat => 'c', msg => "Required command $cmd is not in the \$PATH!" }); } else { $self->logger({ cat => 'c',msg => "Wrong args passed to in_path!" }); } } } elsif ( $opts->{mode} ) { if ( $opts->{mode} eq 'nofatal' ) { $ret = 'present'; } } } return $ret; } sub show_version { my $self = shift; print "RAIDER version $base_conf{'raider_version'}\n"; $self->do_exit(); } sub show_help { my $self = shift; print "USAGE: $0 [--run-jobs|--get-info|--version|--check-api|--force-update-vendor-tools] (flags can be combined) --run-jobs Scan the system for supported raid controllers. If found, install appropriate vendor tool (if needed). Check the status of the array(s) for every detected device on the system. Report if array(s) are degraded. --get-info Collect information on the RAID controller(s), attached disks, as well as information on non raided disks. --check-api Check if [$Raider::Base::base_conf{api_host}] is accessible --force-update-vendor-tools Wipe out existing vendor tools and replace with latest RAIDER supported versions. --version Show RAIDER version.\n"; $self->do_exit(); } sub _handle_stale_lock { my $self = shift; unlink($base_conf{lock_file}); $self->place_lock(); $self->logger({ cat => 'w', msg => "Removed stale lock file [$base_conf{lock_file}] (and placed a new one); Continuing..." }); } sub if_running { my $self = shift; $base_conf{hostname} = Raider::Base->get_hostname(); if ( -e $base_conf{lock_file} ) { open(FH, $base_conf{lock_file}) || $self->logger({ cat => 'c', msg => "Can't open [$base_conf{lock_file}]: $!" }); my $lock_contents = <FH>; close (FH); if ( $lock_contents ) { if ( kill(0 => $lock_contents) ) { $self->logger({ cat => 'c', msg => "Refusing to run, non stale lock file [$base_conf{lock_file}] present.", mode => 'noclean' }); } else { $self->_handle_stale_lock(); } } else { $self->_handle_stale_lock(); } } else { $self->prerun_tasks(); } } sub place_lock { my $self = shift; open(my $fh, '>', $base_conf{lock_file}) || $self->logger({ cat => 'c', msg => "Unable to create lock file [$base_conf{lock_file}]! Got: $!" }); print $fh $$; close($fh) || $self->logger({ cat => 'c', msg => "Unable to close [$base_conf{lock_file}] [$!]" }); } sub do_exit { my $self = shift; my $opts = shift; if ( $opts->{death_type} ) { if ( $opts->{death_type} ne 'noclean' ) { unlink($base_conf{'lock_file'}); exit; } else { exit 1; } } else { print "$opts->{msg}\n" if ( $opts->{msg} ); unlink($base_conf{'lock_file'}); exit; } } sub get_hostname { my $self = shift; if ($self->can_load_module('Sys::Hostname::Long')) { return Sys::Hostname::Long::hostname_long(); } $self->load_module_or_die('Sys::Hostname'); return Sys::Hostname::hostname(); } sub logger { my $self = shift; my $opts = shift; my $cat_type; if ( $opts->{cat} eq 'i' ) { $cat_type = 'INFO:'; } elsif ( $opts->{cat} eq 'c' ) { $cat_type = 'CRIT:'; } elsif ( $opts->{cat} eq 'w' ) { $cat_type = 'WARN:'; } unless ( $base_conf{'not_root'} ) { if ( ! -e $base_conf{'log_file'} ) { open(FH,">$base_conf{'log_file'}") or $self->logger({ cat => 'c', msg => "Unable to create log file! Got: $!" }); close(FH); } } if ( -w $base_conf{'log_file'} ) { my $apiObj = Raider::Notification::API->new(); open (LOG, ">>" , $base_conf{'log_file'}) or $apiObj->send_notification({ no_log => 1, message => "Unable to check RAID health. Can't open $base_conf{'log_file'} for writing:\n$!\nPlease resolve this host problem." }); print LOG "[ " , $self->get_time , " ] $cat_type $opts->{msg}\n"; close (LOG); if ( $opts->{cat} eq 'c' ) { print STDERR "\nFATAL: raider has encountered one or more errors. Be sure to review the log ($base_conf{'log_file'}).\n"; } } else { print "$cat_type $opts->{msg}\n"; } if ( $opts->{cat} eq 'c' ) { if ( $opts->{mode} ) { if ( $opts->{mode} eq 'noclean' ) { $self->do_exit({ death_type => 'noclean' }); } } else { $self->do_exit({ death_type => 'fatal' }); } } } sub sys_cmd_timeout { my $self = shift; my $opts = shift; eval { local $SIG{ALRM} = sub {die "alarm\n"}; alarm $base_conf{'sys_cmd_timeout_val'}; `$opts->{cmd} 2> /dev/null`; alarm 0; }; if ($@) { return 1; } else { return 0; } } sub sys_cmd_ok { my $self = shift; my $opts = shift; if ( $self->sys_cmd_timeout({ cmd => $opts->{cmd} }) ) { if ( $self->sys_cmd_timeout({ cmd => $opts->{cmd} }) ) { return 0; # timed out twice, give up. } } return 1; } sub get_first_line_str { my $self = shift; my $opts = shift; open my($fh), "<", \$opts->{str} or $self->logger({ cat => 'c', msg => "Unable to open [$opts->{str}]: $!" }); my $first_line = <$fh>; close $fh; chomp($first_line); return $first_line; } sub load_module_or_die { my ($self,$module) = @_; eval { (my $mod="$module.pm")=~s|::|/|g; require $mod; }; if (my $e = $@) { my $api = Raider::Notification::API->new(); $api->send_notification({ message => "FATAL: cannot load module [$module]; please resolve this host issue!\n$e" }); $self->logger({cat => 'c', msg => "couldn't load [$module]; notificaiton sent, killing myself now.\n$e"}); } } sub can_load_module { my ($self,$module) = @_; eval { (my $mod="$module.pm")=~s|::|/|g; require $mod; }; if (my $e = $@) { $self->logger({cat => 'i', msg => "(this is not an error in and of itself) unable to load [$module]; notifying caller..\n$e"}); return 0; } return 1; } 1;
Copyright ©2k19 -
Hexid
|
Tex7ure