blob: dc9fd4160c0751dfb47f3ad12d2a514941a4b164 [file] [log] [blame]
#!/usr/bin/env perl
use strict;
use warnings;
use mrw::Targets; # Set of APIs allowing access to parsed ServerWiz2 XML output
use mrw::Inventory; # To get list of Inventory targets
use Getopt::Long; # For parsing command line arguments
use Data::Dumper qw(Dumper); # Dumping blob
use List::Util qw(first);
# Globals
my $force = 0;
my $serverwizFile = "";
my $debug = 0;
my $outputFile = "";
my $verbose = 0;
# Command line argument parsing
GetOptions(
"f" => \$force, # numeric
"i=s" => \$serverwizFile, # string
"o=s" => \$outputFile, # string
"d" => \$debug,
"v" => \$verbose,
)
or printUsage();
if (($serverwizFile eq "") or ($outputFile eq ""))
{
printUsage();
}
# Hashmap of all the LED groups with the properties
my %hashGroup;
# hash of targets to Names that have the FRU Inventory instances
my %invHash;
# Hash of Enclosure Fault LED names and their properties
# These are generally front-fault-led and rear-fault-led
my %encFaults;
# These groups are a must in all the systems.
# Its fine if they don't map to any physical LED
my @defaultGroup = ("BmcBooted", "PowerOn");
# This group contains all the LEDs with the action Blink
my $lampTest = "LampTest";
# API used to access parsed XML data
my $targetObj = Targets->new;
if($verbose == 1)
{
$targetObj->{debug} = 1;
}
if($force == 1)
{
$targetObj->{force} = 1;
}
$targetObj->loadXML($serverwizFile);
print "Loaded MRW XML: $serverwizFile \n";
# Iterate over Inventory and get all the Inventory targets.
my @inventory = Inventory::getInventory($targetObj);
for my $item (@inventory)
{
# Target to Obmc_Name hash.
$invHash{$item->{TARGET}} = $item->{OBMC_NAME};
}
# For debugging purpose.
printDebug("\nList of Inventory targets\n");
foreach my $key (sort keys %invHash)
{
printDebug("$invHash{$key}\n");
}
# Process all the targets in the XML. If the target is associated with a FRU,
# then remember it so that when we do the FRU inventory lookup, we know if
# that Inventory has a LED associated with it or not.
foreach my $target (sort keys %{$targetObj->getAllTargets()})
{
# Some the target instances may *not* have this MRW_TYPE attribute.
if($targetObj->isBadAttribute($target, "MRW_TYPE"))
{
next;
}
# Return true if not populated -or- not present
if("LED" eq $targetObj->getMrwType($target))
{
# Just for clarity.
my $ledTarget = $target;
# OBMC_NAME field of the FRU
# fruPath ex /system/chassis/motherboard/dimm1
# device "dimm1"
my $fruPath = '';
my $device = '';
# Find if this LED is associated with a FRU.
# Example, FAN will have LED on that assembly.
my $conns = $targetObj->findConnections($ledTarget, "LOGICAL_ASSOCIATION");
if ($conns ne "")
{
# This LED is associated with a FRU
for my $conn (@{$conns->{CONN}})
{
my $destTarget = $conn->{DEST_PARENT};
# If we have found this, then that means, we do not need to
# hand cook a group name. delete this value from the inventory
# array
if(exists($invHash{$destTarget}))
{
# This will remove a particular {key, value} pair
$fruPath = $invHash{$destTarget};
printDebug("$destTarget : $fruPath is having associated LED\n");
delete ($invHash{$destTarget});
}
}
# fetch FruName from the device path
$device = getFruName($fruPath);
printDebug("$target; $device has device\n");
}
if($targetObj->isBadAttribute($ledTarget, "CONTROL_GROUPS"))
{
next;
}
# By default, Blink takes higher priority
my $priority = "'Blink'";
# Get the priority. Since rest everything is populated,
# default to Blink than err'ing out. Not checking for
# validity of this since it must be present.
if($targetObj->getAttribute($ledTarget, "LED_PRIORITY") eq "ON")
{
$priority = "'On'";
}
#The MRW instance name must match the LED name in the device tree
my $name = $targetObj->getInstanceName($ledTarget);
# Get if this LED is a ENC-FAULT type.
if(!$targetObj->isBadAttribute($target, "LED_TYPE"))
{
if("ENC-FAULT" eq $targetObj->getAttribute($ledTarget, "LED_TYPE"))
{
$encFaults{$name} = $priority;
}
}
# Defines the LEDs and the Groups that they belong to
my $controlGroup = $targetObj->getAttribute($ledTarget, "CONTROL_GROUPS");
#remove spaces, because serverwiz isn't good at removing them itself
$controlGroup =~ s/\s//g;
my @groups= split(',', $controlGroup); #just a long 16x3 = 48 element list
for (my $i = 0; $i < scalar @groups; $i += 3)
{
if (($groups[$i] ne "NA") && ($groups[$i] ne ""))
{
my $groupName = $groups[$i];
printDebug("$groupName\n");
my $blinkFreq = $groups[$i+1];
my $action = "'On'";
my $period = 0;
# Period in milli seconds
my $dutyCycle = $groups[$i+2];
if($blinkFreq > 0)
{
$action = "'Blink'";
$period = (1 / $blinkFreq) * 1000;
}
# Insert into hash map;
$hashGroup{$groupName}{$name}{"Action"} = $action;
$hashGroup{$groupName}{$name}{"Period"} = $period;
$hashGroup{$groupName}{$name}{"DutyOn"} = $dutyCycle;
$hashGroup{$groupName}{$name}{"Priority"} = $priority;
# Need to update the LampTest group.
$hashGroup{$lampTest}{$name}{"Action"} = "'Blink'";
$hashGroup{$lampTest}{$name}{"Period"} = 1000;
$hashGroup{$lampTest}{$name}{"DutyOn"} = 50;
# Priority of a particular LED needs to stay SAME across
# all groups
$hashGroup{$lampTest}{$name}{"Priority"} = $priority;
}
} # Walk CONTROL_GROUP
} # Has LED target
} # All the targets
# These are the FRUs that do not have associated LEDs. All of these need to be
# mapped to some group, which will be named after this target name and the
# elements of the group are EnclosureFaults Front and Back
printDebug("\n======================================================================\n");
printDebug("\nFRUs that do not have associated LEDs\n");
foreach my $key (sort keys %invHash)
{
my $device = getFruName($invHash{$key});
# For each of these device, the Group record would be this :
my $groupName = $device . "Fault";
printDebug("$device :: $groupName\n");
# Setup roll-up LEDs to the ones that are of type ENC-FAULT
foreach my $led (sort keys %encFaults)
{
$hashGroup{$groupName}{$led}{"Action"} = "'On'";
$hashGroup{$groupName}{$led}{"Period"} = 0;
$hashGroup{$groupName}{$led}{"DutyOn"} = 50;
# Priority of a particular LED needs to stay SAME across
# all groups
$hashGroup{$groupName}{$led}{"Priority"} = $encFaults{$led};
}
}
printDebug("\n======================================================================\n");
my $index = rindex($outputFile, ".");
my $suffix = substr($outputFile, $index + 1);
if (lc($suffix) eq "json")
{
# Generate the JSON file
generateJSONFile();
}
else
{
# Generate the yaml file
generateYamlFile();
}
#------------------------------------END OF MAIN-----------------------
# Gven a '/' separated string, returns the leaf.
# Ex: /a/b/c/d returns device=d
sub getFruName
{
my $path = shift;
my $device = '';
my $lastSlash=rindex($path, '/');
$device=substr($path, $lastSlash+1);
}
sub generateYamlFile
{
my $fileName = $outputFile;
my $groupCopy = '';
my $ledCopy = '';
open(my $fh, '>', $fileName) or die "Could not open file '$fileName' $!";
foreach my $group (sort keys %hashGroup)
{
if($group ne $groupCopy)
{
# If one of these is a default group, then delete it from the array
# that is being maintained to create one by hand if all default ones
# are not defined
my $index = first {$defaultGroup[$_] eq $group} 0..$#defaultGroup;
if (defined $index)
{
splice @defaultGroup, $index, 1;
}
$groupCopy = '';
$ledCopy = '';
}
foreach my $led (sort keys %{ $hashGroup{$group} })
{
foreach my $property (sort keys %{ $hashGroup{$group}{$led}})
{
if($group ne $groupCopy)
{
$groupCopy = $group;
print $fh "$group:\n";
}
print $fh " ";
if($led ne $ledCopy)
{
$ledCopy = $led;
print $fh "$led:\n";
print $fh " ";
}
print $fh " ";
print $fh "$property:";
print $fh " $hashGroup{$group}{$led}{$property}\n";
}
}
}
# If we need to hand create some of the groups, do so now.
foreach my $name (@defaultGroup)
{
print $fh "$name:\n";
}
close $fh;
}
sub generateJSONFile
{
package LEDGroups;
use JSON;
my $JSON = JSON->new->utf8->pretty(1);
$JSON->convert_blessed(1);
sub led
{
my $class = shift;
my $self = {
group => shift,
members => shift,
};
bless $self, $class;
return $self;
}
sub member
{
my $class = shift;
my $self = {
Name => shift,
Action => shift,
DutyOn => shift,
Period => shift,
Priority => shift,
};
bless $self, $class;
return $self;
}
sub TO_JSON {
return { %{ shift() } };
}
my $fileName = $outputFile;
open(my $fh, '>', $fileName) or die "Could not open file '$fileName' $!";
my @leds = ();
foreach my $group (sort keys %hashGroup)
{
my @members = ();
foreach my $led (sort keys %{ $hashGroup{$group} })
{
my $action;
my $dutyOn;
my $period;
my $priority;
if (exists $hashGroup{$group}{$led}{Action})
{
$action = $hashGroup{$group}{$led}{Action};
$action = substr($action, 1, length($action) - 2);
}
if (exists $hashGroup{$group}{$led}{DutyOn})
{
$dutyOn = $hashGroup{$group}{$led}{DutyOn};
}
if (exists $hashGroup{$group}{$led}{Period})
{
$period = $hashGroup{$group}{$led}{Period};
}
if (exists $hashGroup{$group}{$led}{Priority})
{
$priority = $hashGroup{$group}{$led}{Priority};
$priority = substr($priority, 1, length($priority) - 2);
}
my $m = member LEDGroups($led, $action, $dutyOn, $period, $priority);
push @members, $m;
}
my $l = led LEDGroups($group, \@members);
push @leds, $l;
}
my %ledJson = ('leds' => \@leds);
my $json = $JSON->canonical(1)->encode(\%ledJson);
print $fh $json;
close $fh;
}
# Helper function to put debug statements.
sub printDebug
{
my $str = shift;
print "DEBUG: ", $str, "\n" if $debug;
}
# Usage
sub printUsage
{
print "
$0 -i [XML filename] -o [Output filename] [OPTIONS]
Options:
-f = force output file creation even when errors
-d = debug mode
-v = verbose mode - for verbose o/p from Targets.pm
PS: mrw::Targets can be found in https://github.com/open-power/serverwiz/
mrw::Inventory can be found in https://github.com/openbmc/phosphor-mrw-tools/
\n";
exit(1);
}
#------------------------------------END OF SUB-----------------------