0

I have a machine located in a secure environment that does not have access to the internet. I has a base load of CentOS with a handful of additional RPMs. This includes a basic install of PERL with no additional modules. Nor does it have GCC installed so I can't install new modules manually nor use CPAN to install them. Therefore, I need a pure Perl solution.

I've been asked to create a Perl script that will validate that the machine has a specific list of RPMs installed and that they're a specific version or newer.

Here's what I have so far:

#!/usr/bin/perl

use strict;
use warnings;

# This is the list of RPMs to look for on the machine.
my @RPMs = ("bwm-ng",
        "celt051",
        "device-mapper-multipath",
        "device-mapper-multipath-libs",
        "dhcp",
        "dhcp-common",
        "ebtables",
        "freeglut",
        "glusterfs-api",
        "glusterfs-libs",
        "gnutls-utils",
        "gpm",
        "hmaccalc",
        "iftop",
        "iperf",
        "ipsec-tools",
        "iptraf",
        "iscsi-initiator-utils",
        "libsysfs",
        "lm_sensors",
        "lm_sensors-libs",
        "log4cpp",
        "lrzsz",
        "lzop",
        "mcsctrans",
        "minicom",
        "nc",
        "netcf-libs",
        "net-snmp",
        "net-snmp-libs",
        "net-snmp-utils",
        "omping",
        "perl-AppConfig",
        "perl-Pod-POM",
        "perl-Template-Toolkit",
        "pimd",
        "python-lxml",
        "quagga",
        "radvd",
        "smcroute",
        "usbredir",
        "yajl");

# These are the RPM versions that they should be equal to or newer than.
my @RPMVersions = ("bwm-ng-0.6-6.el6.2.x86_64",
        "celt051-0.5.1.3-0.el6.x86_64",
        "device-mapper-multipath-0.4.9-87.el6.x86_64",
        "device-mapper-multipath-libs-0.4.9-87.el6.x86_64",
        "dhcp-4.1.1-49.P1.el6.centos.x86_64",
        "dhcp-common-4.1.1-49.P1.el6.centos.x86_64",
        "ebtables-2.0.9-6.el6.x86_64",
        "freeglut-2.6.0-1.el6.x86_64",
        "glusterfs-api-3.4.0.57rhs-1.el6_5.x86_64",
        "glusterfs-libs-3.4.0.57rhs-1.el6_5.x86_64",
        "gnutls-utils-2.8.5-18.el6.x86_64",
        "gpm-1.20.6-12.el6.x86_64",
        "hmaccalc-0.9.12-2.el6.x86_64",
        "iftop-1.0-0.7.pre4.el6.x86_64",
        "iperf-2.0.5-11.el6.x86_64",
        "ipsec-tools-0.8.0-25.3.x86_64",
        "iptraf-3.0.1-14.el6.x86_64",
        "iscsi-initiator-utils-6.2.0.873-14.el6.x86_64",
        "libsysfs-2.1.0-7.el6.x86_64",
        "lm_sensors-3.1.1-17.el6.x86_64",
        "lm_sensors-libs-3.1.1-17.el6.x86_64",
        "log4cpp-1.0-13.el6_5.1.x86_64",
        "lrzsz-0.12.20-27.1.el6.x86_64",
        "lzop-1.02-0.9.rc1.el6.x86_64",
        "mcsctrans-0.3.1-4.el6.x86_64",
        "minicom-2.3-6.1.el6.x86_64",
        "nc-1.84-24.el6.x86_64",
        "netcf-libs-0.2.4-3.el6.x86_64",
        "net-snmp-5.5-54.el6.x86_64",
        "net-snmp-libs-5.5-54.el6.x86_64",
        "net-snmp-utils-5.5-54.el6.x86_64",
        "omping-0.0.4-1.el6.x86_64",
        "perl-AppConfig-1.66-6.el6.x86_64",
        "perl-Pod-POM-0.25-2.el6.x86_64",
        "perl-Template-Toolkit-2.22-5.el6.x86_64",
        "pimd-2.3.0-1.x86_64",
        "python-lxml-2.2.3-1.1.el6.x86_64",
        "quagga-0.99.23.1-2014082501.x86_64",
        "radvd-1.6-1.el6.x86_64",
        "smcroute-2.0.0-0.x86_64",
        "usbredir-0.5.1-2.el6.x86_64",
        "yajl-1.0.7-3.el6.x86_64");

my $RPMname; #This reprepsents an individual RPM name within the @RPMs array.

foreach $RPMname (@RPMs){ # Loop through the @RPMs array and query the RPM database for each RPM.
    my $cmd = "rpm -qa | grep " . $RPMname;

    my @cmdResults = `$cmd`;

    if (! @cmdResults){
        print "\tMissing RPM: " . $RPMname . "\n\n"; # If the RPM isn't installed; inform the user.
    } else {
        foreach(@cmdResults){
            print "\t" . $_ . "\n"; # Print the version of the RPM that's currently installed.

            # Compare the RPM version that's installed with the corresponding version that should be installed
            # as listed in the @RPMVersions array.
            # write some magic here. <------

        }
    }
}

exit(0);

I've found what appears to be a possible solution but I can't seem to figure out how to adjust the code of fit my scenario.

see here: http://www.perlmonks.org/bare/?node=240384

I can't use RPM::VersionSort or several of the other RPM related modules because of the restrictions that I mentioned above.

Any help would be hugely appreciated.

Thanks!

  • 1
    I'm voting to close this question as off-topic because ServerFault is not a script writing service. – Tim Brigham Sep 9 '16 at 17:45
  • 1
    Perl would certainly work, but have you considered Python? Python RPM bindings will be installed for yum. – John Mahowald Sep 10 '16 at 15:18
2

Some hints

You do't need grep e.g.

rpm -q radvd
radvd-1.9.2-9.el7.x86_64
echo $?
0

If a package is missing $? is 1

rpm -q nc 
package nc is not installed
echo $?
1

You can get the version of an installed rpm package using --queryformat

rpm -q radvd --queryformat "%{VERSION}\n"
1.9.2

There is much more you can do with just rpm - have a look at the rpm.org site.

You may even be able to get away without using perl so take a look at Dennis's answer here on SO.

  • I want to mark this post as the answer since I used some of it for my solution in the end. But I don't know if I should since it doesn't exactly answer the original question. – dutsnekcirf Sep 16 '16 at 16:58
  • It does because it points you in the right direction and we're not a script writing service anyway. – Iain Sep 16 '16 at 17:16
  • I never expected anyone to write the script for me. – dutsnekcirf Sep 16 '16 at 17:46
0

While this doesn't answer my original question I'd like to provide what I ended up with. I've been able to convince those in power to allow me to install the RPM::VersionSort Perl module. So this isn't a Pure Perl solution as I was hoping to find.

Here's what I'm using now for anyone who's interested:

#!/usr/bin/perl

use strict;
use warnings;
use RPM::VersionSort;

my $cmd;
my $cmdResults;
my %installedRPMs; #This will hold a list of all the RPMs from the baseRPMs list that ARE currently installed on the machine.
my @missingRPMs; #This will hold a list of all the RPMs from the baseRPMs list that ARE NOT installed on the system.
my %baseRPMs; #This is the list of RPMs that should be installed on the system and their corresponding version numbers.

%baseRPMs = ("bwm-ng" => "0.6-6.el6.2",
        "celt051" => "0.5.1.3-0.el6",
        "device-mapper-multipath" => "0.4.9-87.el6",
        "device-mapper-multipath-libs" => "0.4.9-87.el6",
        "dhcp" => "4.1.1-49.P1.el6.centos",
        "dhcp-common" => "4.1.1-49.P1.el6.centos", 
        "ebtables" => "2.0.9-6.el6",
        "freeglut" => "2.6.0-1.el6",
        "glusterfs-api" => "3.4.0.57rhs-1.el6_5",
        "glusterfs-libs" => "3.4.0.57rhs-1.el6_5",
        "gnutls-utils" => "3.8.5-18.el6",
        "gpm" => "1.20.6-12.el6",
        "hmaccalc" => "0.9.12-2.el6",
        "iftop" => "1.0-0.7.pre4.el6",
        "iperf" => "2.0.5-11.el6",
        "ipsec-tools" => "0.8.0-25.3",
        "iptraf" => "3.0.1-14.el6",
        "iscsi-initiator-utils" => "6.2.0.873-14.el6",
        "libsysfs" => "2.1.0-7.el6",
        "lm_sensors" => "3.1.1-17.el6",
        "lm_sensors-libs" => "3.1.1-17.el6",
        "log4cpp" => "1.0-13.el6_5.1",
        "lrzsz" => "0.12.20-27.1.el6",
        "lzop" => "1.02-0.9.rc1.el6",
        "mcsctrans" => "0.3.1-4.el6",
        "minicom" => "2.3-6.1.el6",
        "nc" => "1.84-24.el6",
        "netcf-libs" => "0.2.4-3.el6",
        "net-snmp" => "5.5-54.el6",
        "net-snmp-libs" => "5.5-54.el6",
        "net-snmp-utils" => "5.5-54.el6",
        "omping" => "0.0.4-1.el6",
        "perl-AppConfig" => "1.66-6.el6",
        "perl-Pod-POM" => "0.25-2.el6",
        "perl-Template-Toolkit" => "2.22-5.el6",
        "pimd" => "2.3.0-1",
        "python-lxml" => "2.2.3-1.1.el6",
        "quagga" => "0.99.23.1-2014082501",
        "radvd" => "1.6-1.el6",
        "smcroute" => "2.0.0-0",
        "usbredir" => "0.5.1-2.el6",
        "yajl" => "1.0.7-3.el6",
    );

print "The following RPMs and version numbers will be checked against this system:\n";
foreach(keys %baseRPMs){
    print "\t" . $_ . ": " . %baseRPMs($_) . "\n";
}

print "Press any key to continue.";
<STDIN>;

#Loop through the %baseRPMs list and build both the %installedRPMs list as well as the @missingRPMs list.
for my $pkg (keys %baseRPMs){
    $cmd = "rpm -q " . $pkg . " --queryformat \"%{VERSION}-%{RELEASE}\"";
    $cmdResults = `$cmd`;
    if ($cmdResults =~ /not installed/) {
        push @missingRPMs, $pkg;
    } else {
        $installedRPMs{$pkg} = $cmdResults;
    }
}

#Loop through the %installedRPMs list and verify their version numbers against the %baseRPMs list.
foreach (keys %installedRPMs){
    if (exists $baseRPMs{$_}){
        print "Expected: " . $_ . ": " . $baseRPMs{$_} .  "\n";
        print "Installed: " . $_ . ": " . $installedRPMs{$_} .  "\n";
        if (rpmvercmp($installedRPMs{$_}, $baseRPMs{$_}) < 0 ) {
            print "RESULT: !!FAIL!! " . $_ . " version is OLDER than the specified version in the Functional Test Plan.\n";
        } else {
            print "RESULT: PASS. " . $_ . " version is equal to or newer than the specified version in the Functional Test Plan. \n";
        }
        print "-----------------------------------------------------------------------------------------------------\n\n";
    }
}

#Print the list of @missingRPMs.
if (@missingRPMs){
    print "The following RPMs are NOT installed as defined by the Functional Test Plan:\n";
    foreach(@missingRPMs){
        print "\t" . $_ . "\n";
    }
    print "\n*Any missing RPMs indicates the system is NOT built as defined by the Functional Test Plan.*\n";
}

print "\n";

exit(0)

;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.