| #!/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----------------------- |