/
lib
/
raider
/
Raider
/
File Upload :
llllll
Current File: //lib/raider/Raider/Devices.pm
use strict; use warnings; package Raider::Devices; use base qw( Raider::Base ); use Raider::Info; use Raider::Notification::API; use Raider::Notification::Email; use JSON::Tiny qw(decode_json encode_json); use Time::Piece; =head1 NAME Raider::Devices - Handle device installation/removal. =head1 DESCRIPTION =head1 USAGE If you need to use directly: use Raider::Devices; my $devices_obj = Raider::Devices->new(); =head1 METHODS =head2 get_devices() Detects what raid hardware is present, and takes needed action. =head2 get_supported_devices() Returns which devices are supported. =head2 register_device(\%args) Registers device, by placing the devices file. =head2 stale_device(\%args) Removes a device file for a device that is no longer on the system. =head2 remove_device(\%args) Removes the software relating to a device that is no longer on the system. =head2 rm_chkraid(\%args) Removes the old system restore check raid script. =head2 is_blacklisted(\%args) Determines if a device is black listed or not. =head2 install_device(\%args) Installs the software for the passed device. =head2 get_unknown_devices(\%args) Finds any raid hardware that RAIDER doesn't know how to handle. =head2 raider_to_raidalarm_device(string) Given a raider device, translate to the raidalarm name =head2 get_device_ids_remote(string,\%hash) Given a raider device, and the local pci device id definitions, get the device ids from Raidalarm (if conf param local_pci_device_id_cache_duration has not been exceeded). If Raidalarm cannot be reached, return an error. If api_notifications are off, return an error saying so. =head2 get_device_ids_local_or_remote(string,\%hash) Given a raider device and its controller details hashref, get the device ids from RaidAlarm or local. If RaidAlarm should be consulted, but it cannot be reached, return the device ids from local records. =head2 get_local_device_ids_from_file(string) Given a local pci device id file, json decode it and verify it is of expected structure. Return the structure if everything checks out. =cut sub get_device_ids_remote { my ($self, $raider_device, $local_dev_defs) = @_; my $conf_file = $self->read_conf_file(); unless ($conf_file->{api_notifications}) { return {error => 'api_notifications disabled, not using Raidalarm for obtaining device id definitions'}; } # in case local_pci_device_id_cache_duration isnt in config, assign default value my $cache_duration = $conf_file->{local_pci_device_id_cache_duration} //= 28800; # if theres an epoch in local copy, ensure cache isnt expired my $time = localtime; if ($local_dev_defs->{epoch}) { my $seconds_elapsed = $time->epoch - $local_dev_defs->{epoch}; if ($cache_duration > $seconds_elapsed) { $self->logger({ cat => 'i', msg => "using local pci device id definitions as [$cache_duration] seconds has not been exceeded; elapsed [$seconds_elapsed]" }); $local_dev_defs->{from_cache} = 1; return $local_dev_defs; } } else { $self->logger({cat => 'w', msg => "no epoch timestamp in local device id definition; unable to check cache duration!"}); } my $data = {}; eval { my $raidalarm_device = $self->raider_to_raidalarm_device($raider_device); $data = $self->{raidalarm}->get_deviceids_by_device($raidalarm_device); $data->{epoch} = $time->epoch; }; if (my $e = $@) { return {error => "Error reaching RaidAlarm: $e"}; } return $data; } sub raider_to_raidalarm_device { my ($self, $raider_device) = @_; if ($raider_device eq 'Adaptec') { return 'adaptec'; } elsif ($raider_device eq '3ware') { return '3ware'; } elsif ($raider_device eq 'FusionMPTSAS2') { return 'fusion'; } elsif ($raider_device eq 'MegaraidSAS') { return 'megaraid_sas'; } elsif ($raider_device eq 'MegaraidSATA') { return 'megaraid_sata'; } elsif ($raider_device eq 'StorCli') { return 'storcli'; } elsif ($raider_device eq 'PercCli') { return 'percli'; } elsif ($raider_device eq 'PercCli2') { return 'percli'; } $self->logger({cat => 'c', msg => "unknown raider device [$raider_device]"}); } sub get_local_device_ids_from_file { my ($self,$local_def_file) = @_; if (!-e $local_def_file) { $self->logger({cat => 'w', msg => "local definition file [$local_def_file] doesnt exist; local lookups are impossible!"}); return {devices => []}; } open my $fh, "<$local_def_file" or $self->logger({cat => 'c', msg => "Unable to open [$local_def_file]: $!"}); my $local_dev_defs_raw = do { local $/; <$fh> }; close($fh) or $self->logger({cat => 'c', msg => "Unable to close $local_def_file: $!"}); my ($local_dev_defs, $purge_error_msg); eval { $local_dev_defs = decode_json($local_dev_defs_raw); }; if (my $e = $@) { $purge_error_msg = "error decoding [$local_def_file]; it has been purged. Had contents: [$local_dev_defs_raw] error: [$e]"; } elsif (ref($local_dev_defs) ne 'HASH' || ref($local_dev_defs) eq 'HASH' && !$local_dev_defs->{devices}) { $purge_error_msg = "data in [$local_def_file] is of unexpected format; it has been purged. Had contents: [$local_dev_defs_raw]"; } if ($purge_error_msg) { unlink($local_def_file) or $self->logger({cat => 'c', msg => "unable to delete [$local_def_file]: $!"}); $self->logger({cat => 'c', msg => $purge_error_msg}); } return $local_dev_defs; } sub get_device_ids_local_or_remote { my ($self, $device, $cntrl_details) = @_; my $local_def_file = $cntrl_details->{CNTLR_LSPCI_FILE}; my $local_dev_defs = $self->get_local_device_ids_from_file($local_def_file); my @device_ids; if ($cntrl_details->{NO_RAIDALARM_DEVICE_IDS}) { $self->logger({ cat => 'i', msg => "RaidAlarms device id lookups disabled in device config for [$device]; using local" }); return $local_dev_defs->{devices}; } # if already timed out once this run, dont try again to save runtime if ($Raider::Base::base_conf{device_id_fetch_method} && $Raider::Base::base_conf{device_id_fetch_method} eq 'local') { @device_ids = @{$local_dev_defs->{devices}}; } if (scalar @device_ids == 0) { my $remote_device_id_resp = $self->get_device_ids_remote($device,$local_dev_defs); if ($remote_device_id_resp->{error}) { $self->logger({cat => 'w', msg => $remote_device_id_resp->{error}}); $Raider::Base::base_conf{device_id_fetch_method} = 'local'; @device_ids = @{$local_dev_defs->{devices}}; } else { $Raider::Base::base_conf{device_id_fetch_method} = 'RaidAlarm'; @device_ids = @{$remote_device_id_resp->{devices}}; my $from_cache = delete $remote_device_id_resp->{from_cache}; if (!$from_cache) { # save newly fetched remote data open my $fh, ">$local_def_file" or $self->logger({cat => 'c', msg => "Unable to write [$local_def_file]: $!"}); my $encoded_defs = encode_json($remote_device_id_resp); print $fh $encoded_defs; close($fh) or $self->logger({cat => 'c', msg => "Unable to close [$local_def_file]: $!"}); $self->logger({cat => 'i', msg => "Updated local device id definition file [$local_def_file]"}); } } } $self->logger({cat => 'i', msg => "using [$Raider::Base::base_conf{device_id_fetch_method}] device id definitions; device [$device]"}); return \@device_ids; } sub get_devices { my $self = shift; my $force_update_vendor_tools = shift; # MD devices $self->register_device({device => 'Mdraid'}) if ($self->_md_devices_present()); if ($force_update_vendor_tools) { $self->logger({cat => 'w', msg => "get_devices() called with force_update_vendor_tools"}); } my $infoObj = Raider::Info->new(); my @known_devices; my $lspci_output = `lspci -vvv -n 2> /dev/null`; for my $device (@{ $self->get_supported_devices() }) { #no device installs to do for non raided disks.. next if ($device eq 'satascsiata'); my $module = "Raider::Devices::$device"; $self->load_module_or_die($module); my $cntlr_details = $module->get_cntlr_details(); my $do_install = 1; # assume vendor tool needs installed, until proven otherwise my ($package_name,$new_device,$detected); foreach my $key (keys %$cntlr_details) { if ($key eq 'CNTLR_LSPCI_FILE') { my $device_ids = $self->get_device_ids_local_or_remote($device, $cntlr_details); foreach my $device_id (@$device_ids) { push(@known_devices, $device_id); next if ($self->is_blacklisted({id => $device_id})); if ($lspci_output =~ /$device_id/) { $detected = 1; $new_device = 1; } } } elsif ($key eq 'OLD_CHKRAID') { $self->rm_chkraid({old_chkraid => $cntlr_details->{$key}}); } elsif ($key eq 'PKG_NAME') { $package_name = $cntlr_details->{$key}; if ($device eq 'MegaraidSAS') { # if cent version is > 4, install raider-megacli-8. if ( $infoObj->is_redhat() && !$infoObj->is_fedora() ) { if ($infoObj->fetch_cent_ver() > 4) { $package_name = 'raider-MegaCli-8'; } } } elsif ($device eq '3ware') { my $arch = $self->get_arch(); $package_name = $package_name . '-x86_64-lw'; $package_name = $package_name . '-x86-lw' if ($arch eq '32bit'); } } elsif ($key eq 'CLI_CMD') { my ($path_check, $utility_cmd); if ($cntlr_details->{$key} =~ /,/) { my @cmds_to_check_in_path = split(/,/, $cntlr_details->{$key}); $utility_cmd = [@cmds_to_check_in_path]; $path_check = $self->in_path({cmds => $utility_cmd, mode => 'nofatal' }); } else { $utility_cmd = [ $cntlr_details->{$key}, ]; $path_check = $self->in_path({cmds => $utility_cmd, mode => 'nofatal'}); } $do_install = 0 if ($path_check eq 'present'); } elsif ($key eq 'CLI_PATH') { if ( $cntlr_details->{$key} =~ /,/ ) { my @cmds_to_check_in_path = split(/,/, $cntlr_details->{$key}); my $cli_path_multiple_ret = $self->cli_path_multiple(\@cmds_to_check_in_path); $do_install = 0 unless ($cli_path_multiple_ret); } elsif ($cntlr_details->{$key}) { $do_install = 0 if (-e $cntlr_details->{$key}); } } } if ($new_device) { if ($force_update_vendor_tools) { $self->purge_existing_vendor_tool($cntlr_details->{CLI_PATH}); $do_install = 1; } $self->install_device({ do_install => $do_install, cntlr_details => $cntlr_details, package_name => $package_name, }); $self->register_device({device => $device}); } elsif (!$detected) { if (-e "$Raider::Base::base_conf{'jobs_path'}/$device") { $self->remove_device({device => $package_name}); $self->stale_device({device => $device}); } } } ## This always gets placed regardless of raid device. $self->register_device({device => 'satascsiata'}); $self->get_unknown_devices(@known_devices); } sub purge_existing_vendor_tool { my ($self, $vendor_tool_path) = @_; my $target_pkg = $self->pkg_claiming_file($vendor_tool_path); if (!$target_pkg) { $self->logger({cat => 'i', msg => "purge_existing_vendor_tool found no installed tool for device, nothing to do!"}); warn "Cannot update existing vendor tool, as no pre-existing tool exists for [$vendor_tool_path]\n"; return; } $self->logger({cat => 'w', msg => "giving warning before package [$target_pkg] is removed per --purge-existing-vendor-tool..."}); warn "Attempting removal of package [$target_pkg] in 10 seconds per --purge-existing-vendor-tool; ctrl+C now to abort!\n"; sleep 10; $self->logger({cat => 'w', msg => "Attempting removal of package [$target_pkg] since you didn't cancel.."}); my $infoObj = Raider::Info->new(); my $pkg_mngr_cmd = $infoObj->get_pkg_mngr_cmd(); print "Running '$pkg_mngr_cmd remove $target_pkg' (any errors will be logged).\n"; my $stderr = `$pkg_mngr_cmd remove $target_pkg 2>&1 1>/dev/null`; if ($? != 0) { $self->logger({cat => 'c', msg => "Failed to remove utility software with '$pkg_mngr_cmd remove $target_pkg'; got: $stderr"}); } $self->logger({cat => 'w', msg => "package [$target_pkg] has been purged!"}); } sub get_supported_devices { my $self = shift; my $path = '/usr/lib/raider/Raider/Devices/'; my $devices = $self->sysFindFile({ search_path => $path, }); my @supported_devices; for my $device (@$devices) { chomp($device); my @sanitized = split('.pm', $device); # Sometimes loaded servers are causing the find to return blanks. # Return if $found is empty to prevent later issues. my $found = $sanitized[0]; next unless $found; push(@supported_devices, $found); } unless (@supported_devices) { $self->logger({cat => 'c', msg => "found no supported devices in [$path]; cannot continue"}); } return \@supported_devices; } sub register_device { my $self = shift; my $opts = shift; my $device_file = "$Raider::Base::base_conf{'jobs_path'}/$opts->{device}"; if (!-e $device_file) { open(FH, ">$device_file") or $self->logger({cat => 'c', msg => "Unable to place device file, got STDERR; $!"}); close(FH); $self->logger({cat => 'i', msg => "Registered new device $opts->{device}"}); } } sub stale_device { my $self = shift; my $opts = shift; my $device_file = "$Raider::Base::base_conf{'jobs_path'}/$opts->{device}"; $self->logger({cat => 'i', msg => "Device $opts->{device} is no longer on the system, removing."}); unlink($device_file) if (-e $device_file); } sub remove_device { my $self = shift; my $opts = shift; my $infoObj = Raider::Info->new(); my $pkg_mngr_cmd = $infoObj->get_pkg_mngr_cmd(); if ($pkg_mngr_cmd =~ /apt-get/) { $opts->{device} =~ tr/A-Z/a-z/; $opts->{device} =~ s/_/-/g; if ($opts->{device} =~ /storman/) { $opts->{device} = 'storman'; } } print "Running '$pkg_mngr_cmd remove $opts->{device}' (any errors will be logged).\n"; my $stderr = `$pkg_mngr_cmd remove $opts->{device} 2>&1 1>/dev/null`; chomp($stderr); if ($? != 0) { $self->logger({ cat => 'c', msg => "Failed to remove utility software with '$pkg_mngr_cmd remove $opts->{device}'; got: $stderr" }); } else { $self->logger({cat => 'i', msg => "Successfully removed utility software ($opts->{device})"}); } } sub rm_chkraid { my $self = shift; my $opts = shift; if (-e $opts->{old_chkraid}) { unlink($opts->{old_chkraid}); $self->logger({cat => 'i', msg => "Found and removed old chkraid script ($opts->{old_chkraid})."}); } } sub is_blacklisted { my $self = shift; my $opts = shift; if (!-e $Raider::Base::base_conf{blacklist_file}) { open(FH, ">$Raider::Base::base_conf{blacklist_file}") or $self->logger({ cat => 'c', msg => "Unable to create [$Raider::Base::base_conf{blacklist_file}]: $!" }); close(FH) or $self->logger({cat => 'c', msg => "Unable to close [$Raider::Base::base_conf{blacklist_file}]: $!"}); } open(BLACKL_FILE, $Raider::Base::base_conf{blacklist_file}) or $self->logger({ cat => 'c', msg => "Failed writing [$Raider::Base::base_conf{blacklist_file}]: $!" }); my @blacklisted_ids = <BLACKL_FILE>; close(BLACKL_FILE) or $self->logger({cat => 'c', msg => "Unable to close [$Raider::Base::base_conf{blacklist_file}]"}); chomp(@blacklisted_ids); for my $listed (@blacklisted_ids) { if ($opts->{id} eq $listed) { $self->logger({cat => 'i', msg => "Skipping blacklisted device $opts->{id}"}); return 1; } } } sub install_device { my $self = shift; my $opts = shift; my $cntlr_details = $opts->{cntlr_details}; my $pkg_name = $opts->{package_name} || $cntlr_details->{PKG_NAME}; if ($opts->{do_install}) { my $infoObj = Raider::Info->new(); my $pkg_mngr_cmd = $infoObj->get_pkg_mngr_cmd(); if ($pkg_mngr_cmd =~ /apt-get/) { $pkg_name =~ tr/A-Z/a-z/; $pkg_name =~ s/_/-/g; if ($pkg_name =~ /storman/i) { $pkg_name = 'storman'; } } if ($infoObj->fetch_cent_ver() == 5) { if ($pkg_name =~ /StorMan/) { $self->logger({cat => 'i', msg => "CentOS 5 detected, installing required compat-libstdc++-33 for StorMan 6.40 RPM"}); my $stderr = `yum -y install compat-libstdc++-33 2>&1 1>/dev/null`; chomp($stderr); if ( $? != 0 ) { $self->logger({cat => 'c', msg => "Failed to install compat-libstdc++-33 'yum -y install compat-libstdc++-33'; got: $stderr"}); } else { $self->logger({cat => 'i', msg => "Successfully installed CentOS 5 dependency compat-libstdc++-33"}); } } } print "Running '$pkg_mngr_cmd install $pkg_name' (Any errors will be logged).\n"; my $stderr = `$pkg_mngr_cmd install $pkg_name 2>&1 1>/dev/null`; chomp($stderr); if ( $? != 0 ) { $self->logger({cat => 'c', msg => "Failed to install utility software with '$pkg_mngr_cmd install $pkg_name'; got: $stderr"}); } else { $self->logger({cat => 'i', msg => "Successfully installed utility software [$pkg_name]"}); if ( $infoObj->is_redhat() ) { if ( $pkg_name =~ /storman/i ) { system('/etc/init.d/stor_agent stop'); system('chkconfig stor_agent off'); $self->logger({cat => 'i', msg => "Stopped stor_agent and disabled with chkconfig."}); } } elsif ( $infoObj->is_debian() ) { if ( $pkg_name =~ /storman/i ) { system('/etc/init.d/stor_agent stop'); system('update-rc.d -f stor_agent disable'); $self->logger({cat => 'i', msg => "Stopped stor_agent and disabled with update-rc.d."}); } } } } else { my $installed_pkg = $self->pkg_claiming_file($cntlr_details->{CLI_PATH}); $installed_pkg //= 'notInstalled'; $self->logger({ cat => 'i', msg => "Utility software [$cntlr_details->{CLI_PATH}] already present, skipping install phase... installed: [$installed_pkg] latest: [$pkg_name] updates can be forced with --force-update-vendor-tools", }); } } sub pkg_claiming_file { my ($self, $cli_path) = @_; return if (!-e $cli_path); require Raider::Info; my $infoObj = Raider::Info->new; my $distro_based = $infoObj->get_os(); my $pkg; if ($distro_based eq 'redhat') { $pkg = `rpm -qf $cli_path`; } elsif ($distro_based eq 'debian') { # bash: /bin/bash $pkg = `dpkg -S $cli_path`; $pkg = $1 if ($pkg =~ /(\S+):/); } chomp($pkg); return $pkg; } sub get_unknown_devices { my ($self, @known_devices) = @_; my @cur_devices; for my $line (split /^/, `lspci -n`) { if ($line =~ /^\S+\s+0104:\s+(\S+)/) { push(@cur_devices, $1); } elsif ($line =~ /^\S+\s+Class\s+0104:\s+(\S+)/) { push(@cur_devices, $1); } } my @unknown_devices; for my $disc_device (@cur_devices) { if ($self->is_blacklisted({id => $disc_device})) { $self->logger({cat => 'i', msg => "Discovered blacklisted device $disc_device, skipping..."}); next; } else { my $known = 0; for my $defined_device (@known_devices) { if ($disc_device eq $defined_device) { $known = 1; } } unless ($known) { $self->logger({cat => 'w', msg => "Unknown Raid Device with numeric device id [$disc_device] was found."}); push(@unknown_devices, $disc_device); } } } my $notIfAPI = Raider::Notification::API->new(); my $notIfEmail = Raider::Notification::Email->new(); for my $unknown_device (@unknown_devices) { if ($notIfAPI->can_alert({notify_type => 'unknown_devices'})) { my $notifMsg = "[$Raider::Base::base_conf{hostname}] [$self->{uniq_id}]: Unknown raid device with numeric device id [$unknown_device].\n"; $notifMsg .= "Known pci device ids were fetched from: [$Raider::Base::base_conf{device_id_fetch_method}]\n"; if ($notIfAPI->can_api()) { $self->logger({cat => 'i', msg => 'API host is online, using API notifications.'}); $notIfAPI->send_notification({message => $notifMsg}); } else { $notIfEmail->send_notification({ subject => "Unknown Raid Device on [$Raider::Base::base_conf{hostname}] [$self->{uniq_id}]", message => $notifMsg }); } $notIfAPI->place_alert({notify_type => 'unknown_devices'}); } else { $self->logger({cat => 'w', msg => 'Notification for unknown_devices surpressed, alert file for today already exists.'}); } } } sub _md_devices_present { my $self = shift; my $opts = shift; my @dev_entries = glob("/dev/*"); for my $device (@dev_entries) { return 1 if ($device =~ /\/md\d+/); } return 0; } 1;
Copyright ©2k19 -
Hexid
|
Tex7ure