1 Title: Automatically upgrading server firmware on Dell PowerEdge
5 <p>At work we have heaps of servers. I believe the total count is
6 around 1000 at the moment. To be able to get help from the vendors
7 when something go wrong, we want to keep the firmware on the servers
8 up to date. If the firmware isn't the latest and greatest, the
9 vendors typically refuse to start debugging any problems until the
10 firmware is upgraded. So before every reboot, we want to upgrade the
11 firmware, and we would really like everyone handling servers at the
12 university to do this themselves when they plan to reboot a machine.
13 For that to happen we at the unix server admin group need to provide
14 the tools to do so.</p>
16 <p>To make firmware upgrading easier, I am working on a script to
17 fetch and install the latest firmware for the servers we got. Most of
18 our hardware are from Dell and HP, so I have focused on these servers
19 so far. This blog post is about the Dell part.</P>
21 <p>On the Dell FTP site I was lucky enough to find
22 <a href="ftp://ftp.us.dell.com/catalog/Catalog.xml.gz">an XML file</a>
23 with firmware information for all 11th generation servers, listing
24 which firmware should be used on a given model and where on the FTP
25 site I can find it. Using a simple perl XML parser I can then
26 download the shell scripts Dell provides to do firmware upgrades from
27 within Linux and reboot when all the firmware is primed and ready to
28 be activated on the first reboot.</p>
30 <p>This is the Dell related fragment of the perl code I am working on.
31 Are there anyone working on similar tools for firmware upgrading all
32 servers at a site? Please get in touch and lets share resources.</p>
38 use File::Temp qw(tempdir);
40 # Install needed RHEL packages if missing
42 'XML::Simple' => 'perl-XML-Simple',
44 for my $module (keys %rhelmodules) {
47 my $pkg = $rhelmodules{$module};
48 system("yum install -y $pkg");
53 my $errorsto = 'pere@hungry.com';
59 sub run_firmware_script {
60 my ($opts, $script) = @_;
62 print STDERR "fail: missing script name\n";
65 print STDERR "Running $script\n\n";
67 if (0 == system("sh $script $opts")) { # FIXME correct exit code handling
68 print STDERR "success: firmware script ran succcessfully\n";
70 print STDERR "fail: firmware script returned error\n";
74 sub run_firmware_scripts {
75 my ($opts, @dirs) = @_;
76 # Run firmware packages
78 print STDERR "info: Running scripts in $dir\n";
79 opendir(my $dh, $dir) or die "Unable to open directory $dir: $!";
80 while (my $s = readdir $dh) {
81 next if $s =~ m/^\.\.?/;
82 run_firmware_script($opts, "$dir/$s");
90 print STDERR "info: Downloading $url\n";
91 system("wget --quiet \"$url\"");
96 my $product = `dmidecode -s system-product-name`;
99 if ($product =~ m/PowerEdge/) {
101 # on RHEL, these pacakges are needed by the firwmare upgrade scripts
102 system('yum install -y compat-libstdc++-33.i686 libstdc++.i686 libxml2.i686 procmail');
104 my $tmpdir = tempdir(
108 fetch_dell_fw('catalog/Catalog.xml.gz');
109 system('gunzip Catalog.xml.gz');
110 my @paths = fetch_dell_fw_list('Catalog.xml');
111 # -q is quiet, disabling interactivity and reducing console output
114 for my $url (@paths) {
117 run_firmware_scripts($fwopts, $tmpdir);
119 print STDERR "error: Unsupported Dell model '$product'.\n";
120 print STDERR "error: Please report to $errorsto.\n";
124 print STDERR "error: Unsupported Dell model '$product'.\n";
125 print STDERR "error: Please report to $errorsto.\n";
131 my $url = "ftp://ftp.us.dell.com/$path";
135 # Using ftp://ftp.us.dell.com/catalog/Catalog.xml.gz, figure out which
136 # firmware packages to download from Dell. Only work for Linux
137 # machines and 11th generation Dell servers.
138 sub fetch_dell_fw_list {
139 my $filename = shift;
141 my $product = `dmidecode -s system-product-name`;
143 my ($mybrand, $mymodel) = split(/\s+/, $product);
145 print STDERR "Finding firmware bundles for $mybrand $mymodel\n";
147 my $xml = XMLin($filename);
149 for my $bundle (@{$xml->{SoftwareBundle}}) {
150 my $brand = $bundle->{TargetSystems}->{Brand}->{Display}->{content};
151 my $model = $bundle->{TargetSystems}->{Brand}->{Model}->{Display}->{content};
153 if ("ARRAY" eq ref $bundle->{TargetOSes}->{OperatingSystem}) {
154 $oscode = $bundle->{TargetOSes}->{OperatingSystem}[0]->{osCode};
156 $oscode = $bundle->{TargetOSes}->{OperatingSystem}->{osCode};
158 if ($mybrand eq $brand && $mymodel eq $model && "LIN" eq $oscode)
160 @paths = map { $_->{path} } @{$bundle->{Contents}->{Package}};
163 for my $component (@{$xml->{SoftwareComponent}}) {
164 my $componenttype = $component->{ComponentType}->{value};
166 # Drop application packages, only firmware and BIOS
167 next if 'APAC' eq $componenttype;
169 my $cpath = $component->{path};
170 for my $path (@paths) {
171 if ($cpath =~ m%/$path$%) {
172 push(@paths, $cpath);
180 <p>The code is only tested on RedHat Enterprise Linux, but I suspect
181 it could work on other platforms with some tweaking. Anyone know a
182 index like Catalog.xml is available from HP for HP servers? At the
183 moment I maintain a similar list manually and it is quickly getting