#!/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"); # 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; } # 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-----------------------