2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-21 19:22:05 +00:00

765 lines
21 KiB
Perl

#!/usr/bin/env perl -w
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#####################################################
#
# xCAT plugin package to handle kernel modules
#
#####################################################
package xCAT_plugin::kmodules;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
require xCAT::Table;
require xCAT::Utils;
require xCAT::TableUtils;
require Data::Dumper;
require Getopt::Long;
require xCAT::MsgUtils;
use File::Path qw(mkpath rmtree);
use File::Basename qw(basename);
use Text::Balanced qw(extract_bracketed);
use Safe;
my $evalcpt = new Safe;
use strict;
use warnings;
#
# Globals
#
#------------------------------------------------------------------------------
=head1 kmodules
This program module file performs kernel module functions
Supported commands:
lskmodules -- List the kernel modules in one or more:
osimage.driverupdatesrc (duds or rpms)
kitcomponent.driverpacks (rpms in repodir)
osdistro (kernel-<kernvers> rpms)
osdistroupdate (kernel-<kernvers> rpms)
=cut
#------------------------------------------------------------------------------
=head2 Kernel Modules Support
=cut
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------
=head3 handled_commands
Return a list of commands handled by this plugin
=cut
#-----------------------------------------------------------------------------
sub handled_commands {
return {
lskmodules => "kmodules"
};
}
#----------------------------------------------------------------------------
=head3 preprocess_request
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
# sub preprocess_request {
#
# NOT REQUIRED -- no hierarchy for this command
# my $req = shift;
# return [$req];
# }
#----------------------------------------------------------------------------
=head3 process_request
Process the kernel modules commands
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
sub process_request {
$::request = shift;
$::CALLBACK = shift;
$::SUBREQ = shift;
my $ret;
# globals used by all subroutines.
$::command = $::request->{command}->[0];
$::args = $::request->{arg};
$::stdindata = $::request->{stdin}->[0];
# figure out which cmd and call the subroutine to process
if ($::command eq "lskmodules") {
$ret = &lskmodules($::request);
}
return $ret;
}
#----------------------------------------------------------------------------
=head3 lskmodules_usage
Arguments:
Returns:
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
# display the usage
sub lskmodules_usage {
my $rsp;
push @{ $rsp->{data} },
"\nUsage: lskmodules - List kernel modules in specified input \n";
push @{ $rsp->{data} },
" lskmodules [-V|--verbose] [-x|--xml|--XML] [-c|--kitcomponent kit_comp1,kit_comp2,...] [-o|--osdistro os_distro] [-u|--osdistroupdate os_distro_update] [-i|--osimage osimage] \n ";
push @{ $rsp->{data} }, " lskmodules [-h|--help|-?] \n";
push @{ $rsp->{data} },
" lskmodules [-v|--version] \n ";
xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
return 0;
}
#----------------------------------------------------------------------------
=head3 processArgs
Process the command line
Arguments:
Returns:
0 - OK
1 - just print usage
2 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
sub processArgs {
if (defined($::args) && @{$::args}) {
@ARGV = @{$::args};
}
# parse the options
# options can be bundled up like -vV, flag unsupported options
Getopt::Long::Configure("bundling", "no_ignore_case", "no_pass_through");
Getopt::Long::GetOptions(
'help|h|?' => \$::opt_h,
'kitcomponent|c=s' => \$::opt_c,
'osimage|i=s' => \$::opt_i,
'osdistro|o=s' => \$::opt_o,
'osdistroupdate|u=s' => \$::opt_u,
'verbose|V' => \$::opt_V,
'version|v' => \$::opt_v,
'xml|XML|x' => \$::opt_x,
);
# Option -h for Help
if (defined($::opt_h)) {
return 2;
}
# Option -v for version
if (defined($::opt_v)) {
my $rsp;
my $version = xCAT::Utils->Version();
push @{ $rsp->{data} }, "$::command - $version\n";
xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
return 1; # no usage - just exit
}
# Option -V for verbose output
if (defined($::opt_V)) {
$::verbose = 1;
$::VERBOSE = 1;
}
if (!defined($::opt_c) &&
!defined($::opt_i) &&
!defined($::opt_o) &&
!defined($::opt_u)) {
my $rsp;
push @{ $rsp->{data} }, "Specify a search location \n";
xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
return 2;
}
my $more_input = shift(@ARGV);
if (defined($more_input)) {
my $rsp;
push @{ $rsp->{data} }, "Invalid input: $more_input \n";
xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
return 2;
}
return 0;
}
#----------------------------------------------------------------------------
=head3 lskmodules
Support for listing kernel modules
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
sub lskmodules {
my $rc = 0;
# process the command line
$rc = &processArgs;
if ($rc != 0) {
# rc: 0 - ok, 1 - return, 2 - help, 3 - error
if ($rc != 1) {
&lskmodules_usage;
}
return ($rc - 1);
}
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "Running lskmodules command... ";
xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
}
# Get all the rpms and img files to search based on command input
my @sources = &set_sources;
if (!(@sources)) {
my $rsp;
push @{ $rsp->{data} }, "No input search source found.";
xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
return 1;
}
# Get the list of kernel modules in each rpm/img file
my @modlist;
foreach my $source (@sources) {
if ($source =~ /^dud:/) {
$source =~ s/^dud://;
push(@modlist, &mods_in_img($source));
} else {
my $osver = xCAT::Utils->osver();
if ($osver =~ /ubuntu/) {
$source =~ s/^deb://;
} else {
$source =~ s/^rpm://;
}
push(@modlist, &mods_in_rpm($source));
}
}
@modlist = &remove_duplicate_mods(\@modlist);
# Return the module list for this rpm/img file
my $rsp;
foreach my $mn (@modlist) {
if ($::opt_x) {
my %data_entry;
$data_entry{module}->{name} = $mn->{name};
$data_entry{module}->{description} = $mn->{description};
push @{ $rsp->{data} }, \%data_entry;
} else {
push @{ $rsp->{data} }, $mn->{name} . ': ' . $mn->{description};
}
}
if ($rsp) {
$::CALLBACK->($rsp);
}
return $rc;
}
#----------------------------------------------------------------------------
=head3 set_sources
return array of input kernel module sources
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
sub set_sources {
my $installdir = xCAT::TableUtils->getInstallDir;
my @sources;
my %src_data;
my $order = 1;
# OS images (osimage.driverupdatesrc)
if (defined($::opt_i)) {
my $litab = xCAT::Table->new('linuximage');
foreach my $li (split(',', $::opt_i)) {
my ($li_entry) = $litab->getAttribs({ 'imagename' => $li }, ('driverupdatesrc'));
if (!($li_entry)) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "No driverupdatesrc attribute for osimage $li found. Skipping. ";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
}
foreach my $dus (split(',', $li_entry->{'driverupdatesrc'})) {
my $base_dus = basename($dus);
if (defined($src_data{$base_dus})) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "Duplicate source $base_dus found in input. Skipping $dus entry in osimage &li driverupdatesrc. Using from $src_data{$base_dus}{'sourcemsg'} instead.";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
} else {
$src_data{$base_dus}{'sourcemsg'} = "osimage $li driverupdatesrc entry $dus";
$src_data{$base_dus}{'source'} = $dus;
$src_data{$base_dus}{'order'} = $order++;
}
}
}
$litab->close;
}
# Kit Components (kitcomp.driverpacks)
if (defined($::opt_c)) {
my $kctab = xCAT::Table->new('kitcomponent');
my $krtab = xCAT::Table->new('kitrepo');
foreach my $kc (split(',', $::opt_c)) {
my ($kc_entry) = $kctab->getAttribs({ 'kitcompname' => $kc }, ('kitreponame', 'driverpacks'));
if (!($kc_entry)) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "No driverpacks attribute for kitcomponent $kc found. Skipping.";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
}
my ($kr_entry) = $krtab->getAttribs({ 'kitreponame' => $kc_entry->{'kitreponame'} }, ('kitrepodir'));
if (!($kr_entry)) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "Kitrepo $kc_entry->{'kitreponame'} not found in database. Error in kitcomponent definition for $kc. Skipping.";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
}
foreach my $dp (split(',', $kc_entry->{'driverpacks'})) {
if (defined($src_data{$dp})) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "Duplicate source $dp found in input. Skipping kitcomponent $kc driverpack entry $dp. Using from $src_data{$dp}{'sourcemsg'} instead.";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
} else {
$src_data{$dp}{'sourcemsg'} = "kitcomponent $kc, kitrepo $kc_entry->{'kitreponame'}";
$src_data{$dp}{'source'} = $kr_entry->{'kitrepodir'} . '/' . $dp;
$src_data{$dp}{'order'} = $order++;
}
}
}
$kctab->close;
}
# OS distro update
if (defined($::opt_u)) {
my $outab = xCAT::Table->new('osdistroupdate');
foreach my $ou (split(',', $::opt_u)) {
my ($ou_entry) = $outab->getAttribs({ 'osupdatename' => $ou }, ('dirpath', 'basename'));
if (!($ou_entry) || !(defined($ou_entry->{'dirpath'}))) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "No dirpath attribute for osdistroupdate $ou found. Skipping.";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
}
my $dirpath = $ou_entry->{'dirpath'};
my @kernel_rpms = ();
if ($ou_entry->{'basename'} =~ /^ubuntu/)
{
@kernel_rpms = `find $dirpath/pool/main/l/linux/ -name *deb`;
}
else
{
@kernel_rpms = `find $dirpath -name kernel-*.rpm`;
}
foreach my $krpm (@kernel_rpms) {
chomp($krpm);
my $base_krpm = basename($krpm);
if (defined($src_data{$base_krpm})) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "Duplicate source $base_krpm found in input. Skipping $krpm in osdistroupdate $ou. Using from $src_data{$base_krpm}{'sourcemsg'} instead.";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
}
$src_data{$base_krpm}{'sourcemsg'} = "osdistroupdate $ou rpm $krpm";
$src_data{$base_krpm}{'source'} = $krpm;
$src_data{$base_krpm}{'order'} = $order++;
}
}
$outab->close;
}
# OS distro
if (defined($::opt_o)) {
my $odtab = xCAT::Table->new('osdistro');
foreach my $od (split(',', $::opt_o)) {
my ($od_entry) = $odtab->getAttribs({ 'osdistroname' => $od }, ('dirpaths', 'basename'));
if (!($od_entry)) {
# try building dirpath from distro_name/local_arch
my $arch = `uname -m`;
chomp($arch);
$arch = "x86" if ($arch =~ /i.86$/);
my $dirpath = $installdir . '/' . $od . '/' . $arch;
if (!(-e $dirpath)) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "No dirpaths attribute for osdistro $od found. Skipping. ";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
}
my @kernel_rpms = ();
if ($od_entry->{'basename'} =~ /ubuntu/)
{
@kernel_rpms = `find $dirpath/pool/main/l/linux/ -name *deb`;
}
else
{
@kernel_rpms = `find $dirpath -name kernel-*.rpm`;
}
foreach my $krpm (@kernel_rpms) {
chomp($krpm);
my $base_krpm = basename($krpm);
if (!defined($src_data{$base_krpm})) {
$src_data{$base_krpm}{'sourcemsg'} = "osdistro $od default rpm $krpm";
$src_data{$base_krpm}{'source'} = $krpm;
$src_data{$base_krpm}{'order'} = $order++;
}
}
} else {
foreach my $dirpath (split(',', $od_entry->{'dirpaths'})) {
my @kernel_rpms = ();
if ($od_entry->{'basename'} =~ /ubuntu/)
{
@kernel_rpms = `find $dirpath/pool/main/l/linux/ -name *deb`;
}
else
{
@kernel_rpms = `find $dirpath -name kernel-*.rpm`;
}
#my @kernel_rpms = `find $dirpath -name kernel-*.rpm`;
foreach my $krpm (@kernel_rpms) {
chomp($krpm);
my $base_krpm = basename($krpm);
if (defined($src_data{$base_krpm})) {
if ($::VERBOSE) {
my $rsp;
push @{ $rsp->{data} }, "Duplicate source $base_krpm found in input. Skipping $krpm in osdistro $od. Using from $src_data{$base_krpm}{'sourcemsg'} instead.";
xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
}
next;
}
$src_data{$base_krpm}{'sourcemsg'} = "osdistro $od rpm $krpm";
$src_data{$base_krpm}{'source'} = $krpm;
$src_data{$base_krpm}{'order'} = $order++;
}
}
}
}
$odtab->close;
}
foreach my $base_source (sort { $src_data{$a}{'order'} <=> $src_data{$b}{'order'} } keys %src_data) {
push @sources, $src_data{$base_source}{'source'};
}
if ($::VERBOSE && @sources) {
my $rsp;
push @{ $rsp->{data} }, "Searching the following locations for kernel modules: ";
push @{ $rsp->{data} }, @sources;
xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
}
return @sources;
}
#----------------------------------------------------------------------------
=head3 mods_in_rpm
return hash of module names/descriptions found in rpm
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
sub mods_in_rpm {
my $krpm = shift;
my @modlist;
my $tmp_path = "/tmp/lskmodules_expanded_rpm";
mkpath($tmp_path);
if (-r $krpm) {
my $osver = xCAT::Utils->osver();
if ($osver =~ /ubuntu/) {
if (system("cd $tmp_path; dpkg -X $krpm $tmp_path > /dev/null 2>&1 ; cd - > /dev/null 2>&1")) {
my $rsp;
push @{ $rsp->{data} }, "Unable to extract files from the deb package $krpm.";
xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
rmtree($tmp_path);
return; }
} else {
if (system("cd $tmp_path; rpm2cpio $krpm | cpio -idum > /dev/null 2>&1 ; cd - > /dev/null 2>&1")) {
my $rsp;
push @{ $rsp->{data} }, "Unable to extract files from the rpm $krpm.";
xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
rmtree($tmp_path);
return;
}
}
} else {
my $rsp;
push @{ $rsp->{data} }, "Unable to read rpm $krpm.";
xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
rmtree($tmp_path);
return;
}
my @ko_files = `find $tmp_path -regextype posix-egrep -regex ".*/*\.ko(\.xz)?"`;
foreach my $ko (@ko_files) {
my %mod;
chomp($ko);
my $name = basename($ko);
my $desc = `modinfo -d $ko`;
chomp($desc);
if ($desc =~ /^\s*$/) {
$desc = " ";
}
$mod{name} = $name;
$mod{description} = $desc;
push(@modlist, \%mod);
}
rmtree($tmp_path);
return @modlist;
}
#----------------------------------------------------------------------------
=head3 mods_in_img
return hash of module names/descriptions found in
driver update image
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
sub mods_in_img {
my $img_file = shift;
my @modlist;
my $mnt_path = "/tmp/lskmodules_mnt";
mkpath($mnt_path);
my $rc = system("mount -o loop $img_file $mnt_path");
if ($rc) {
my $rsp;
push @{ $rsp->{data} }, "Mount of driver disk image $img_file failed";
xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
rmtree($mnt_path);
return;
}
my @ko_files = `find $mnt_path -name *.ko`;
foreach my $ko (@ko_files) {
my %mod;
chomp($ko);
my $name = basename($ko);
my $desc = `modinfo -d $ko`;
chomp($desc);
if ($desc =~ /^\w*$/) {
$desc = " ";
}
$mod{name} = $name;
$mod{description} = $desc;
push(@modlist, \%mod);
}
$rc = system("umount $mnt_path");
rmtree($mnt_path);
return @modlist;
}
#----------------------------------------------------------------------------
=head3 remove_duplicate_mods
return array of unique module names/descriptions hashes from input array
Arguments:
Returns:
0 - OK
1 - error
Globals:
Error:
Example:
Comments:
=cut
#-----------------------------------------------------------------------------
sub remove_duplicate_mods {
my $modlist = shift;
my %unique_names;
my @return_list;
foreach my $mn (@$modlist) {
if (defined($unique_names{ $mn->{name} })) { next; }
$unique_names{ $mn->{name} } = $mn->{description};
}
foreach my $un (sort keys(%unique_names)) {
my %mod;
$mod{name} = $un;
$mod{description} = $unique_names{$un};
push(@return_list, \%mod);
}
return @return_list;
}
1;