Created
January 23, 2015 13:16
-
-
Save asquelt/b3c86f4b76fa5b542cb9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
#/home/mirror/bin/updateinfo.pl -a i386 -H rhn.redhat.com -u xxx -p xxx -c rhel-i386-server-5 -x /home/mirror/db/rhel-i386-server-5.xml -d /home/mirror/db/rhel-i386-server-5.db | |
#/home/mirror/bin/updateinfo.pl -a x86_64 -H rhn.redhat.com -u xxx -p xxx -c rhel-x86_64-server-5 -x /home/mirror/db/rhel-x86_64-server-5.xml -d /home/mirror/db/rhel-x86_64-server-5.db | |
#/home/mirror/bin/updateinfo.pl -a i386 -H rhn.redhat.com -u xxx -p xxx -c rhel-i386-server-6 -x /home/mirror/db/rhel-i386-server-6.xml -d /home/mirror/db/rhel-i386-server-6.db | |
#/home/mirror/bin/updateinfo.pl -a x86_64 -H rhn.redhat.com -u xxx -p xxx -c rhel-x86_64-server-6 -x /home/mirror/db/rhel-x86_64-server-6.xml -d /home/mirror/db/rhel-x86_64-server-6.db | |
use strict; | |
$|=1; | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
# For use of the RedHat Network Hosted API see https://rhn.redhat.com/rhn/apidoc/ | |
# needed perl modules | |
my @modCheck = qw( | |
Frontier::Client | |
HTML::Entities | |
Getopt::Long | |
Crypt::SSLeay | |
Time::HiRes | |
Pod::Usage | |
DBI | |
DBD::SQLite | |
XML::Writer | |
IO::File | |
); | |
for (@modCheck) { | |
eval "use $_"; if ($@) { | |
die "$_ is not installed | |
CPAN install: perl -MCPAN -e 'install $_'\n"; | |
} | |
} | |
# code begin | |
my( $opt_h, $opt_l, $opt_a, $opt_c, $opt_u, $opt_p, $opt_H, $opt_x, $opt_d, $opt_f, $PROGNAME); | |
my $arch; | |
my $rhchannel; | |
my $rhusername; | |
my $rhpassword; | |
my $HOST; | |
my $client; | |
my $session; | |
$PROGNAME = "updateinfo.pl"; | |
sub usage ($); | |
sub list_channels(); | |
sub do_login(); | |
Getopt::Long::Configure('bundling'); | |
GetOptions | |
("h|help" => \$opt_h, | |
"l|list" => \$opt_l, | |
"a|arch=s" => \$opt_a, | |
"c|channel=s" => \$opt_c, | |
"u|rhnusername=s" => \$opt_u, | |
"p|rhnpassword=s" => \$opt_p, | |
"H|rhnhostname=s" => \$opt_H, | |
"x|filename=s" => \$opt_x, | |
"d|database=s" => \$opt_d, | |
"f|fullupdate" => \$opt_f); | |
pod2usage(2) if ($opt_h); | |
if ($opt_l) { | |
($opt_u) || usage("RedHat Network username not specified\n"); | |
$rhusername = $opt_u; | |
($opt_p) || usage("RedHat Network password not specified\n"); | |
$rhpassword = $opt_p; | |
($opt_H) || usage("RedHat Network hostname not specified\n"); | |
$HOST = $opt_H; | |
list_channels(); | |
exit; | |
} | |
($opt_c) || usage("arch not specified\n"); | |
$arch = $opt_a; | |
($opt_c) || usage("RedHat channel not specified\n"); | |
$rhchannel = $opt_c; | |
($opt_u) || usage("RedHat Network username not specified\n"); | |
$rhusername = $opt_u; | |
($opt_p) || usage("RedHat Network password not specified\n"); | |
$rhpassword = $opt_p; | |
($opt_H) || usage("RedHat Network hostname not specified\n"); | |
$HOST = $opt_H; | |
($opt_x) || usage("updateinfo.xml filename not specified\n"); | |
my $updateinfofile = $opt_x; | |
($opt_d) || usage("sqlite file not specified\n"); | |
my $updateinfodbfile = $opt_d; | |
# login and get session | |
do_login(); | |
# start time measurement | |
my $t0 = [Time::HiRes::gettimeofday()]; | |
my $dbh = DBI->connect( "dbi:SQLite:dbname=$updateinfodbfile", "", "" ); | |
$dbh->{PrintError} = 0; | |
$dbh->{RaiseError} = 1; | |
$dbh->do( "CREATE TABLE IF NOT EXISTS channelerrata ( advisory VARCHAR ( 255 ) PRIMARY KEY, synopsis VARCHAR ( 255 ), issue_date VARCHAR ( 255 ), update_date VARCHAR ( 255 ));" ); | |
$dbh->do( "CREATE TABLE IF NOT EXISTS errata ( advisory VARCHAR ( 255 ) PRIMARY KEY, type VARCHAR ( 255 ) , description VARCHAR ( 255 ) );" ); | |
$dbh->do( "CREATE TABLE IF NOT EXISTS misc ( id INTEGER PRIMARY KEY, lastrundate DATE, channelname VARCHAR ( 255 ) );"); | |
$dbh->do( "CREATE TABLE IF NOT EXISTS erratabugzilla(id INTEGER PRIMARY KEY, advisory VARCHAR ( 255 ), bugzilla_id VARCHAR ( 255 ), summary VARCHAR ( 255 ));" ); | |
$dbh->do( "CREATE TABLE IF NOT EXISTS erratacves( id INTEGER PRIMARY KEY, advisory VARCHAR ( 255 ), cve_id VARCHAR ( 255 ) );" ); | |
$dbh->do( "CREATE TABLE IF NOT EXISTS erratapackages( id INTEGER PRIMARY KEY, advisory VARCHAR ( 255 ), name VARCHAR ( 255 ), version VARCHAR ( 255 ), release VARCHAR ( 255 ), epoch VARCHAR ( 255 ), arch_label VARCHAR ( 255 ), file VARCHAR ( 255 ), md5sum VARCHAR ( 255 ));" ); | |
my $lastrundate; | |
my $lastrundatevalue; | |
unless ($opt_f) { | |
$lastrundate = $dbh->prepare( "SELECT lastrundate FROM misc WHERE id=1;"); | |
undef $lastrundatevalue; | |
eval { | |
$lastrundatevalue = $dbh->selectrow_array( $lastrundate,{}, ); | |
}; | |
print "Getting erratas from after $lastrundatevalue\n" if $lastrundatevalue; | |
} | |
else | |
{ | |
print "Performing a full update.\n"; | |
} | |
# get list of Erratas for a given channel | |
my $channelerrata = $client->call('channel.software.listErrata', $session, $rhchannel, $lastrundatevalue); | |
print "Processing erratas:\n"; | |
for my $errata (@$channelerrata) { | |
eval { | |
$dbh->do( "INSERT INTO channelerrata(advisory, synopsis, issue_date, update_date) VALUES( '$errata->{errata_advisory}','$errata->{errata_synopsis}', '$errata->{errata_issue_date}', '$errata->{errata_update_date}');" ); | |
}; | |
if ($@) { | |
print "Updating errata $errata->{errata_advisory} in local database\n"; | |
$dbh->do( "UPDATE channelerrata SET advisory='$errata->{errata_advisory}', synopsis='$errata->{errata_synopsis}', issue_date='$errata->{errata_issue_date}', update_date='$errata->{errata_update_date}' WHERE advisory='$errata->{errata_advisory}';" ); | |
} | |
else { | |
print "Inserting errata $errata->{errata_advisory} in local database\n"; | |
} | |
# get extra details about an errata | |
my $package; | |
my $sql; | |
eval { | |
$package = $client->call('errata.getDetails', $session, $errata->{errata_advisory}) || die; | |
}; | |
if ($@) { | |
print "WARNING: cannot get $errata->{errata_advisory} details\n"; | |
} else { | |
$sql = "INSERT INTO errata(advisory,type, description) VALUES('$errata->{errata_advisory}', '$package->{errata_type}', '" . HTML::Entities::encode_entities_numeric($package->{errata_description}) . "');"; | |
eval { | |
$dbh->do( $sql); | |
}; | |
if ($@) { | |
print "Updating errata $errata->{errata_advisory} details in local database\n"; | |
$dbh->do( "UPDATE errata SET advisory='$errata->{errata_advisory}',type='$package->{errata_type}', description='" . HTML::Entities::encode_entities_numeric($package->{errata_description}) . "' WHERE advisory='$errata->{errata_advisory}';"); | |
} | |
else { | |
print "Inserting errata $errata->{errata_advisory} details in local database\n"; | |
} | |
} | |
# get a list (if any) of bugzilla references for a given errata | |
my $erratabugzillaFixes; | |
my $erratabugzillaFixeserr=0; | |
eval { | |
$erratabugzillaFixes = $client->call('errata.bugzillaFixes', $session, $errata->{errata_advisory}); | |
}; | |
if ($@) { | |
print "WARNING: cannot get $errata->{errata_advisory} bugzilla\n"; | |
$erratabugzillaFixeserr=1; | |
} | |
# get a list (if any) of CVE references for a given errata | |
my $erratacves; | |
my $erratacveserr=0; | |
eval { | |
$erratacves = $client->call('errata.listCves', $session, $errata->{errata_advisory}); | |
$erratacveserr=1; | |
}; | |
if ($@) { | |
print "WARNING: cannot get $errata->{errata_advisory} CVEs\n"; | |
} | |
if (! $erratabugzillaFixeserr) { | |
while ((my $bugzilla_id, my $bug_summary) = each(%$erratabugzillaFixes)){ | |
my $myid; | |
eval { | |
$myid = $dbh->selectall_arrayref( "SELECT id FROM erratabugzilla WHERE advisory='$errata->{errata_advisory}' AND bugzilla_id='$bugzilla_id';"); | |
}; | |
if (@$myid == 0) { | |
print "Inserting bugzilla $bugzilla_id in local database\n"; | |
$sql = "INSERT INTO erratabugzilla(advisory, bugzilla_id, summary) VALUES ('$errata->{errata_advisory}', '$bugzilla_id', '". HTML::Entities::encode_entities_numeric($bug_summary) . "');"; | |
$dbh->do( $sql ); | |
} | |
else { | |
print "Updating bugzilla $bugzilla_id in local database\n"; | |
$dbh->do( "UPDATE erratabugzilla SET advisory='$errata->{errata_advisory}', bugzilla_id='$bugzilla_id', summary='" . HTML::Entities::encode_entities_numeric($bug_summary) . "' WHERE advisory='$errata->{errata_advisory}' AND bugzilla_id='$bugzilla_id';"); | |
} | |
} | |
} elsif (! $erratacveserr) { | |
for my $erratacve (@$erratacves) { | |
my $mycveid; | |
eval { | |
$mycveid = $dbh->selectall_arrayref( "SELECT id FROM erratacves WHERE advisory='$errata->{errata_advisory}' AND cve_id='$erratacve';"); | |
}; | |
if (@$mycveid == 0) { | |
print "Inserting $erratacve in local database\n"; | |
$sql = "INSERT INTO erratacves(advisory,cve_id) VALUES ('$errata->{errata_advisory}', '$erratacve');"; | |
$dbh->do( $sql ); | |
} | |
else { | |
print "Updating cves $erratacve in local database\n"; | |
$dbh->do( "UPDATE erratacves SET advisory='$errata->{errata_advisory}',cve_id='$erratacve' WHERE advisory='$errata->{errata_advisory}' AND cve_id='$erratacve';" ); | |
} | |
} | |
} | |
# get the list of program packages for the errata | |
my $erratapackages; | |
eval { | |
$erratapackages = $client->call('errata.listPackages', $session, $errata->{errata_advisory}); | |
}; | |
if ($@) { | |
print "WARNING: cannot get $errata->{errata_advisory} packages\n"; | |
} else { | |
$sql = "DELETE FROM erratapackages WHERE advisory='$errata->{errata_advisory}';"; | |
eval { | |
$dbh->do( $sql ); | |
}; | |
for my $erratapackage (@$erratapackages) { | |
# only select channel architecture and noarch packages | |
if($erratapackage->{package_arch_label} =~ m/$arch|noarch/i) { | |
$sql = "INSERT INTO erratapackages(advisory, name, version, release, epoch, arch_label, file, md5sum) VALUES ('$errata->{errata_advisory}', '$erratapackage->{package_name}', '$erratapackage->{package_version}', '$erratapackage->{package_release}', '$erratapackage->{package_epoch}', '$erratapackage->{package_arch_label}', '$erratapackage->{package_file}', '$erratapackage->{package_md5sum}');"; | |
eval { | |
$dbh->do( $sql ); | |
}; | |
print "Inserting erratapackages $erratapackage->{package_name} in local database\n"; | |
} | |
} | |
} | |
} | |
my $channelname; | |
my $channellist = $client->call('channel.listSoftwareChannels', $session); | |
for my $channel (@$channellist) { | |
if($channel->{channel_label} =~ m/^$rhchannel$/i) { | |
$channelname = $channel->{channel_name}; | |
} | |
} | |
eval { | |
$dbh->do( "INSERT INTO misc VALUES (1, date('now'), '$channelname'); "); | |
}; | |
if ($@) { | |
$dbh->do( "UPDATE misc SET ID=1, lastrundate=date('now'), channelname='$channelname' WHERE ID=1; "); | |
} | |
print "Generating xml file.\n"; | |
my $xmloutput = new IO::File(">$updateinfofile"); | |
my $writer = new XML::Writer(OUTPUT => $xmloutput); | |
$writer->xmlDecl("UTF-8"); | |
$writer->startTag("updates"); | |
my $erratarows = $dbh->selectall_arrayref("SELECT advisory,type,description FROM errata ORDER BY advisory ASC;"); | |
foreach my $erratarow (@$erratarows) { | |
my ($advisory,$type,$description) = @$erratarow; | |
my $xml_update_type; | |
if($type =~ m/bug fix/i) { $xml_update_type = "bugfix"; } | |
if($type =~ m/security/i) { $xml_update_type = "security"; } | |
if($type =~ m/enhancement/i) { $xml_update_type = "enhancement"; } | |
$writer->startTag( "update", from => 'security@redhat.com', status => 'final', type => $xml_update_type, version => '1'); | |
$writer->dataElement( 'id', $advisory); | |
my $channelerratarows = $dbh->selectall_arrayref( "SELECT synopsis, issue_date, update_date FROM channelerrata WHERE advisory='$advisory';"); | |
foreach my $channelerratarow (@$channelerratarows) { | |
my ($synopsis, $issue_date, $update_date) = @$channelerratarow; | |
$writer->dataElement( 'title', $synopsis); | |
$writer->emptyTag( 'issued', 'date' => $issue_date); | |
$writer->emptyTag( 'updated', 'date' => $update_date); | |
} | |
my $erratarows = $dbh->selectall_arrayref( "SELECT type, description FROM errata where advisory='$advisory';" ); | |
foreach my $erratarow (@$erratarows) { | |
my ($type, $description) = @$erratarow; | |
$writer->dataElement( 'description', $description); | |
} | |
my $erratabugzillaFixesrows = $dbh->selectall_arrayref( "SELECT bugzilla_id, summary FROM erratabugzilla WHERE advisory='$advisory';" ); | |
my $erratabugzillaCount = scalar(@$erratabugzillaFixesrows); | |
my $erratacvesrows = $dbh->selectall_arrayref( "SELECT cve_id FROM erratacves WHERE advisory='$advisory';" ); | |
my $erratacvesCount = scalar(@$erratacvesrows); | |
if ($erratabugzillaCount > 0 || $erratacvesCount > 0) { | |
$writer->startTag( "references" ); | |
foreach my $erratabugzillaFixesrow (@$erratabugzillaFixesrows) { | |
my ($bugzilla_id, $summary) = @$erratabugzillaFixesrow; | |
$writer->dataElement( 'reference', $summary, 'href' => "http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=$bugzilla_id", 'id' => $bugzilla_id, 'type' => 'bugzilla'); | |
} | |
foreach my $erratacvesrow (@$erratacvesrows) { | |
my ($cve_id) = @$erratacvesrow; | |
$writer->emptyTag( 'reference', 'href' => "http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=$cve_id", 'id' => $cve_id, 'type' => 'cve'); | |
} | |
$writer->endTag("references"); | |
} | |
else { | |
$writer->emptyTag( 'references'); | |
} | |
$writer->startTag( "pkglist"); | |
$writer->startTag( "collection", short => $rhchannel); | |
my $xmlchannelname = $dbh->prepare( "SELECT channelname FROM misc WHERE id=1;"); | |
my $xmlchannelnamevalue = $dbh->selectrow_array( $xmlchannelname,{}, ); | |
$writer->dataElement( 'name', $xmlchannelnamevalue); | |
my $erratapackagesrows = $dbh->selectall_arrayref( "SELECT name, version, release, epoch, arch_label, file, md5sum FROM erratapackages WHERE advisory='$advisory';" ); | |
foreach my $erratapackagesrow (@$erratapackagesrows) { | |
my ($name, $version, $release, $epoch, $arch_label, $file, $md5sum) = @$erratapackagesrow; | |
$writer->startTag( "package", name => $name, version => $version, release => $release, epoch => $epoch, arch => $arch_label, src => ""); | |
$writer->dataElement( 'filename', $file); | |
$writer->dataElement( 'sum', $md5sum, type => "md5"); | |
$writer->endTag("package"); | |
} | |
$writer->endTag("collection"); | |
$writer->endTag("pkglist"); | |
$writer->endTag; | |
} | |
$writer->endTag; | |
$writer->end(); | |
$xmloutput->close(); | |
$dbh->disconnect(); | |
print "Finished:\n"; | |
printf ("run time was: %d seconds.\n", Time::HiRes::tv_interval($t0)); | |
exit; | |
# usage help text | |
sub usage ($) { | |
print @_; | |
print "Usage: $PROGNAME -a <arch> -c <rhn channel> -u <rhn username> -p <rhn password> -H <rhn hostname> -x <filename> -d <sqlitedbfilename>\n"; | |
print "Example: $PROGNAME -a i386 -c rhel-i386-server-5 -u myuser\@domain.com -p secret -H rhn.redhat.com -x updateinfo.xml -d updateinfo.db\n"; | |
exit | |
} | |
# list channels | |
sub list_channels() { | |
# login and get a session key | |
do_login(); | |
# get the list of available software channels for the user | |
my $channellist = $client->call('channel.listSoftwareChannels', $session); | |
for my $channel (@$channellist) { | |
printf ("%-40s, %s\n",$channel->{channel_label}, $channel->{channel_name}); | |
} | |
} | |
# login routine | |
sub do_login() { | |
# create a new XML RPC client | |
$client = new Frontier::Client(url => "https://$HOST/rpc/api"); | |
# login and get a valid session key | |
$session = $client->call('auth.login', $rhusername, $rhpassword); | |
} | |
__END__ | |
=head1 NAME | |
updateinfo.pl - Generating the updateinfo.xml | |
=head1 SYNOPSIS | |
updateinfo.pl [options] | |
Options: | |
-h or --help brief help message | |
--man full documentation | |
-l or --list List available channels for you | |
-a or --arch Platform architecture | |
-c or --channel RHN Channel | |
-u or --rhnusername Your RHN username | |
-p or --rhnpassword Your RHN password | |
-H or --rhnhost RedHat Network host address (or a RHN Satelite host) | |
-x or --filename output xml filename | |
-d or --database output sqlite database filename | |
-f or --fullupdate Perform a full update | |
=head1 OPTIONS | |
=over 8 | |
=item B<--help> | |
Print a brief help message and exits. | |
=item B<--man> | |
Prints the manual page and exits. | |
=item B<--list> | |
List available channels for you. | |
Usefull for new users of mrepo and you do not know the exact channel name. | |
=item B<--arch> | |
Platform architecture. | |
Ie. i386, x86_64, ppc, ia64, s390, s390x, alpha, sparc, iSeries, pSeries. | |
=item B<--channel> | |
RedHat Network software channel | |
Ie. rhel-i386-server-5 | |
Use --list to get a list of available channel names. | |
=item B<--rhnusername> | |
Your RedHat Network username. | |
You must have a valid RedHat subscription. | |
=item B<--rhnpassword> | |
Your valid Redhat Network password. | |
You must have a valid RedHat subscription. | |
=item B<--rhnhost> | |
Target RedHat Network hostname. | |
A RedHat Satelite server can also be used. | |
=item B<--filename> | |
Output xml file. | |
Ie. updateinfo.xml. | |
=item B<--database> | |
Output sqlite file | |
Ie. updateinfo.db | |
=item B<--fullupdate> | |
Perform a full update. | |
This will download all erratas from RedHat. | |
If omitted the script will only download erratas | |
released after the last time the script has been run. | |
=back | |
=head1 DESCRIPTION | |
B<This program> will generate a updateinfo.xml file | |
for mrepo for use of the yum-security yum plugin. | |
=head1 AUTHOR | |
Bjarne Saltbaek <arnebjarne72@hotmail.com> | |
=cut |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment