X-Git-Url: http://pere.pagekite.me/gitweb/homepage.git/blobdiff_plain/887fd9f3e9774a0dc8425b762be5d14dd04589af..18cd81271e2bc7cffb81d037a78e4c24df1b5cdf:/blog/archive/2011/11/index.html diff --git a/blog/archive/2011/11/index.html b/blog/archive/2011/11/index.html index ec39d0d744..ca8ed4c6b7 100644 --- a/blog/archive/2011/11/index.html +++ b/blog/archive/2011/11/index.html @@ -139,6 +139,206 @@ til aktuelle artikler og innlegg om temaet.

+
+
+ Automatically upgrading server firmware on Dell PowerEdge +
+
+ 21st November 2011 +
+
+

At work we have heaps of servers. I believe the total count is +around 1000 at the moment. To be able to get help from the vendors +when something go wrong, we want to keep the firmware on the servers +up to date. If the firmware isn't the latest and greatest, the +vendors typically refuse to start debugging any problems until the +firmware is upgraded. So before every reboot, we want to upgrade the +firmware, and we would really like everyone handling servers at the +university to do this themselves when they plan to reboot a machine. +For that to happen we at the unix server admin group need to provide +the tools to do so.

+ +

To make firmware upgrading easier, I am working on a script to +fetch and install the latest firmware for the servers we got. Most of +our hardware are from Dell and HP, so I have focused on these servers +so far. This blog post is about the Dell part.

+ +

On the Dell FTP site I was lucky enough to find +an XML file +with firmware information for all 11th generation servers, listing +which firmware should be used on a given model and where on the FTP +site I can find it. Using a simple perl XML parser I can then +download the shell scripts Dell provides to do firmware upgrades from +within Linux and reboot when all the firmware is primed and ready to +be activated on the first reboot.

+ +

This is the Dell related fragment of the perl code I am working on. +Are there anyone working on similar tools for firmware upgrading all +servers at a site? Please get in touch and lets share resources.

+ +

+#!/usr/bin/perl
+use strict;
+use warnings;
+use File::Temp qw(tempdir);
+BEGIN {
+    # Install needed RHEL packages if missing
+    my %rhelmodules = (
+        'XML::Simple' => 'perl-XML-Simple',
+        );
+    for my $module (keys %rhelmodules) {
+        eval "use $module;";
+        if ($@) {
+            my $pkg = $rhelmodules{$module};
+            system("yum install -y $pkg");
+            eval "use $module;";
+        }
+    }
+}
+my $errorsto = 'pere@hungry.com';
+
+upgrade_dell();
+
+exit 0;
+
+sub run_firmware_script {
+    my ($opts, $script) = @_;
+    unless ($script) {
+        print STDERR "fail: missing script name\n";
+        exit 1
+    }
+    print STDERR "Running $script\n\n";
+
+    if (0 == system("sh $script $opts")) { # FIXME correct exit code handling
+        print STDERR "success: firmware script ran succcessfully\n";
+    } else {
+        print STDERR "fail: firmware script returned error\n";
+    }
+}
+
+sub run_firmware_scripts {
+    my ($opts, @dirs) = @_;
+    # Run firmware packages
+    for my $dir (@dirs) {
+        print STDERR "info: Running scripts in $dir\n";
+        opendir(my $dh, $dir) or die "Unable to open directory $dir: $!";
+        while (my $s = readdir $dh) {
+            next if $s =~ m/^\.\.?/;
+            run_firmware_script($opts, "$dir/$s");
+        }
+        closedir $dh;
+    }
+}
+
+sub download {
+    my $url = shift;
+    print STDERR "info: Downloading $url\n";
+    system("wget --quiet \"$url\"");
+}
+
+sub upgrade_dell {
+    my @dirs;
+    my $product = `dmidecode -s system-product-name`;
+    chomp $product;
+
+    if ($product =~ m/PowerEdge/) {
+
+        # on RHEL, these pacakges are needed by the firwmare upgrade scripts
+        system('yum install -y compat-libstdc++-33.i686 libstdc++.i686 libxml2.i686 procmail');
+
+        my $tmpdir = tempdir(
+            CLEANUP => 1
+            );
+        chdir($tmpdir);
+        fetch_dell_fw('catalog/Catalog.xml.gz');
+        system('gunzip Catalog.xml.gz');
+        my @paths = fetch_dell_fw_list('Catalog.xml');
+        # -q is quiet, disabling interactivity and reducing console output
+        my $fwopts = "-q";
+        if (@paths) {
+            for my $url (@paths) {
+                fetch_dell_fw($url);
+            }
+            run_firmware_scripts($fwopts, $tmpdir);
+        } else {
+            print STDERR "error: Unsupported Dell model '$product'.\n";
+            print STDERR "error: Please report to $errorsto.\n";
+        }
+        chdir('/');
+    } else {
+        print STDERR "error: Unsupported Dell model '$product'.\n";
+        print STDERR "error: Please report to $errorsto.\n";
+    }
+}
+
+sub fetch_dell_fw {
+    my $path = shift;
+    my $url = "ftp://ftp.us.dell.com/$path";
+    download($url);
+}
+
+# Using ftp://ftp.us.dell.com/catalog/Catalog.xml.gz, figure out which
+# firmware packages to download from Dell.  Only work for Linux
+# machines and 11th generation Dell servers.
+sub fetch_dell_fw_list {
+    my $filename = shift;
+
+    my $product = `dmidecode -s system-product-name`;
+    chomp $product;
+    my ($mybrand, $mymodel) = split(/\s+/, $product);
+
+    print STDERR "Finding firmware bundles for $mybrand $mymodel\n";
+
+    my $xml = XMLin($filename);
+    my @paths;
+    for my $bundle (@{$xml->{SoftwareBundle}}) {
+        my $brand = $bundle->{TargetSystems}->{Brand}->{Display}->{content};
+        my $model = $bundle->{TargetSystems}->{Brand}->{Model}->{Display}->{content};
+        my $oscode;
+        if ("ARRAY" eq ref $bundle->{TargetOSes}->{OperatingSystem}) {
+            $oscode = $bundle->{TargetOSes}->{OperatingSystem}[0]->{osCode};
+        } else {
+            $oscode = $bundle->{TargetOSes}->{OperatingSystem}->{osCode};
+        }
+        if ($mybrand eq $brand && $mymodel eq $model && "LIN" eq $oscode)
+        {
+            @paths = map { $_->{path} } @{$bundle->{Contents}->{Package}};
+        }
+    }
+    for my $component (@{$xml->{SoftwareComponent}}) {
+        my $componenttype = $component->{ComponentType}->{value};
+
+        # Drop application packages, only firmware and BIOS
+        next if 'APAC' eq $componenttype;
+
+        my $cpath = $component->{path};
+        for my $path (@paths) {
+            if ($cpath =~ m%/$path$%) {
+                push(@paths, $cpath);
+            }
+        }
+    }
+    return @paths;
+}
+
+ +

The code is only tested on RedHat Enterprise Linux, but I suspect +it could work on other platforms with some tweaking. Anyone know a +index like Catalog.xml is available from HP for HP servers? At the +moment I maintain a similar list manually and it is quickly getting +outdated.

+ +
+
+ + + Tags: debian, english. + + +
+
+
+

RSS Feed