blob: 75c42410f549adf09b67011d1b9c352a3020f03e [file] [log] [blame]
#!/usr/bin/env perl
#Creates the OpenBMC inventory from ServerWiz output XML.
#Basically, the inventory includes anything with a FRU name,
#plus some other specific things we always look for since we
#need more than just FRUs.
use strict;
use XML::Simple;
use mrw::Targets;
use Getopt::Long;
use JSON;
my $serverwizFile;
my $outputFile, my $target;
my $fruName, my $type;
my @items, my %item, my %inventory;
#Ensure we never pick these up
my %skipFRUTypes = (OCC => 1);
#We always want the targets with these Types
my %includedTypes = ("CORE" => 1);
#We always want the targets with these MRW Types
my %includedTargetTypes = ("chip-sp-bmc" => 1,
"chip-apss-psoc" => 1);
#These are never considered FRUs
my %notFRUTypes = ("CORE" => 1);
GetOptions("x=s" => \$serverwizFile,
"o=s" => \$outputFile)
or printUsage();
if ((not defined $serverwizFile) || (not defined $outputFile)) {
printUsage();
}
my $targetObj = Targets->new;
$targetObj->loadXML($serverwizFile);
foreach $target (sort keys %{ $targetObj->getAllTargets() })
{
$type = $targetObj->getType($target);
if (exists $skipFRUTypes{$type}) {
next;
}
$fruName = "";
if (!$targetObj->isBadAttribute($target, "FRU_NAME")) {
$fruName = $targetObj->getAttribute($target,"FRU_NAME");
}
my $targetType = $targetObj->getTargetType($target);
#We're looking for FRUs, and a few other required parts
if (($fruName ne "") || (exists $includedTargetTypes{$targetType}) ||
(exists $includedTypes{$type}))
{
$item{name} = $target;
$item{orig_name} = $target;
$item{fru_type} = $type;
$item{target_type} = $targetType;
if (($fruName ne "") && (not exists $notFRUTypes{$type})) {
$item{is_fru} = 1;
} else {
$item{is_fru} = 0;
}
push @items, { %item };
}
}
#Hardcode the entries that will never be in the MRW
#TODO: openbmc/openbmc#596 Remove when BIOS version is stored elsewhere.
$inventory{'<inventory_root>/system/bios'} =
{is_fru => 1, fru_type => 'SYSTEM'};
#TODO: openbmc/openbmc#597 Remove when misc FRU data is stored elsewhere.
$inventory{'<inventory_root>/system/misc'} =
{is_fru => 0, fru_type => 'SYSTEM'};
transform(\@items, \%inventory);
#Encode in JSON and write it out
my $json = JSON->new;
$json->indent(1);
$json->canonical(1);
my $text = $json->encode(\%inventory);
open(FILE, ">$outputFile") or die "Unable to create $outputFile\n";
print FILE $text;
close FILE;
print "Created $outputFile\n";
#Apply OpenBMC naming conventions to the Serverwiz names
sub transform
{
my $items = shift @_;
my $inventory = shift @_;
removeConnectors($items);
removeProcModule($items);
renameSegmentWithType("PROC", "cpu", $items);
renameSegmentWithType("SYS", "system", $items);
renameType("SYS", "SYSTEM", $items);
renameSegmentWithType("NODE", "chassis", $items);
renameType("NODE", "SYSTEM", $items);
renameSegmentWithTargetType("card-motherboard", "motherboard", $items);
renameTypeWithTargetType("card-motherboard", "MAIN_PLANAR", $items);
renameType("MEMBUF", "MEMORY_BUFFER", $items);
renameType("FSP", "BMC", $items);
removeCoreParentChiplet($items);
removeInstNumIfOneInstPresent($items);
removeHyphensFromInstanceNum($items);
for my $i (@$items) {
my $name = "<inventory_root>".$i->{name};
delete $i->{name};
delete $i->{orig_name};
delete $i->{target_type};
$inventory{$name} = { %$i };
}
}
#Renames a segment in all target names based on its type
#
#For example:
# renameSegmentWithType("PROC", "foo", $items)
# would change
# /sys-0/node-0/motherboard-0/module-0/cpu/core0
# to
# /sys-0/node-0/motherboard-0/module-0/foo/core0
# assuming /sys-0/.../cpu had type PROC.
sub renameSegmentWithType
{
my $type = shift @_;
my $newSegment = shift @_;
my $items = shift @_;
my %segmentsToRename;
for my $item (@$items) {
my @segments = split('/', $item->{orig_name});
my $target = "";
for my $s (@segments) {
if (length($s) > 0) {
$target .= "/$s";
my $curType = "";
if (!$targetObj->isBadAttribute($target, "TYPE")) {
$curType = $targetObj->getType($target);
}
if ($curType eq $type) {
if (not defined $segmentsToRename{$target}) {
my ($oldSegment) = $target =~ /\b(\w+)(-\d+)?$/;
$segmentsToRename{$target}{old} = $oldSegment;
$segmentsToRename{$target}{new} = $newSegment;
}
}
}
}
}
for my $s (keys %segmentsToRename) {
for my $item (@$items) {
$item->{name} =~
s/$segmentsToRename{$s}{old}/$segmentsToRename{$s}{new}/;
}
}
}
#Renames a segment in all target names based on its target type
#
#For example:
# renameSegmentWithType("PROC", "foo", $items)
# would change
# /sys-0/node-0/motherboard-0/module-0/cpu/core0
# to
# /sys-0/node-0/motherboard-0/module-0/foo/core0
# assuming /sys-0/.../cpu had target type PROC.
sub renameSegmentWithTargetType
{
my $type = shift @_;
my $newSegment = shift @_;
my $items = shift @_;
my %segmentsToRename;
for my $item (@$items) {
my @segments = split('/', $item->{orig_name});
my $target = "";
for my $s (@segments) {
if (length($s) > 0) {
$target .= "/$s";
my $curType = $targetObj->getTargetType($target);
if ($curType eq $type) {
if (not defined $segmentsToRename{$target}) {
my ($oldSegment) = $target =~ /\b(\w+)(-\d+)?$/;
$segmentsToRename{$target}{old} = $oldSegment;
$segmentsToRename{$target}{new} = $newSegment;
}
}
}
}
}
for my $s (keys %segmentsToRename) {
for my $item (@$items) {
$item->{name} =~
s/$segmentsToRename{$s}{old}/$segmentsToRename{$s}{new}/;
}
}
}
#Remove the core's parent chiplet, after moving
#the chiplet's instance number to the core.
#Note: Serverwiz always puts the core on a chiplet
sub removeCoreParentChiplet
{
my $items = shift @_;
for my $item (@$items) {
if ($item->{fru_type} eq "CORE") {
$item->{name} =~ s/\w+-(\d+)\/(\w+)-\d+$/$2-$1/;
}
}
}
#Remove path segments that are connectors
sub removeConnectors
{
my $items = shift @_;
my %connectors;
my $item;
for $item (@$items) {
my @segments = split('/', $item->{name});
my $target = "";
for my $s (@segments) {
if (length($s) > 0) {
$target .= "/$s";
my $class = $targetObj->getAttribute($target, "CLASS");
if ($class eq "CONNECTOR") {
if (not exists $connectors{$target}) {
$connectors{$target} = 1;
}
}
}
}
}
#remove the connector segments out of the path
#Reverse sort so we start with connectors further out
for my $connector (sort {$b cmp $a} keys %connectors) {
for $item (@$items) {
if ($item->{name} =~ /$connector\b/) {
my ($inst) = $connector =~ /-(\d+)$/;
my ($card) = $item->{name};
$card =~ s/^$connector\///;
#add the connector instance to the child card
$card =~ s/^(\w+)-\d+/$1-$inst/;
#remove the connector segment from the path
my $base = $connector;
$base =~ s/\w+-\d+$//;
$item->{name} = $base . $card;
}
}
}
}
#Remove the processor module card from the path name.
#Note: Serverwiz always outputs proc_socket-X/module-Y/proc.
# where proc_socket, module, and proc can be any name
# We already transormed it to module-X/proc.
# Our use requires proc-X.
# Note: No multichip modules in plan for OpenPower systems.
sub removeProcModule
{
my $items = shift @_;
my $procName = "";
#Find the name of the processor used in this model
for my $item (@$items) {
if ($item->{fru_type} eq "PROC") {
($procName) = $item->{name} =~ /\b(\w+)$/;
last;
}
}
#Now remove it from every instance that it's a part of
if ($procName eq "") {
print "Could not find the name of the processor in this system\n";
} else {
for my $item (@$items) {
$item->{name} =~ s/\w+-(\d+)\/$procName/$procName-$1/;
}
}
}
sub renameType
{
my $old = shift @_;
my $new = shift @_;
my $items = shift @_;
for my $item (@$items) {
$item->{fru_type} =~ s/$old/$new/;
}
}
sub renameTypeWithTargetType
{
my $targetType = shift@_;
my $newType = shift @_;
my $items = shift @_;
for my $item (@$items) {
if ($item->{target_type} eq $targetType) {
$item->{fru_type} = $newType;
}
}
}
sub removeHyphensFromInstanceNum
{
my $items = shift @_;
for my $item (@$items) {
$item->{name} =~ s/-(\d+)\b/$1/g;
}
}
sub renameSegment
{
my $old = shift @_;
my $new = shift @_;
my $items = shift @_;
for my $item (@$items) {
$item->{name} =~ s/\b$old\b/$new/;
}
}
sub removeInstNumIfOneInstPresent
{
my $items = shift @_;
my %instanceHash;
my $segment, my $item;
for $item (@$items) {
my @segments = split('/', $item->{name});
for $segment (@segments) {
my ($s, $inst) = $segment =~ /(\w+)-(\d+)/;
if (defined $s) {
if (not exists $instanceHash{$s}) {
$instanceHash{$s}{inst} = $inst;
}
else {
if ($instanceHash{$s}{inst} ne $inst) {
$instanceHash{$s}{keep} = 1;
}
}
}
}
}
for my $segment (keys %instanceHash) {
if (not exists $instanceHash{$segment}{keep}) {
for $item (@$items) {
$item->{name} =~ s/$segment-\d+/$segment/;
}
}
}
}
sub printUsage
{
print "inventory.pl -x [XML filename] -o [output filename]\n";
exit(1);
}