1#!/usr/bin/env perl 2use strict; 3use warnings; 4 5use mrw::Targets; # Set of APIs allowing access to parsed ServerWiz2 XML output 6use mrw::Inventory; # To get list of Inventory targets 7use Getopt::Long; # For parsing command line arguments 8use Data::Dumper qw(Dumper); # Dumping blob 9use List::Util qw(first); 10 11# Globals 12my $force = 0; 13my $serverwizFile = ""; 14my $debug = 0; 15my $outputFile = ""; 16my $verbose = 0; 17 18# Command line argument parsing 19GetOptions( 20"f" => \$force, # numeric 21"i=s" => \$serverwizFile, # string 22"o=s" => \$outputFile, # string 23"d" => \$debug, 24"v" => \$verbose, 25) 26or printUsage(); 27 28if (($serverwizFile eq "") or ($outputFile eq "")) 29{ 30 printUsage(); 31} 32 33# Hashmap of all the LED groups with the properties 34my %hashGroup; 35 36# hash of targets to Names that have the FRU Inventory instances 37my %invHash; 38 39# Hash of Enclosure Fault LED names and their properties 40# These are generally front-fault-led and rear-fault-led 41my %encFaults; 42 43# These groups are a must in all the systems. 44# Its fine if they don't map to any physical LED 45my @defaultGroup = ("BmcBooted", "PowerOn"); 46 47# This group contains all the LEDs with the action Blink 48my $lampTest = "LampTest"; 49 50# API used to access parsed XML data 51my $targetObj = Targets->new; 52if($verbose == 1) 53{ 54 $targetObj->{debug} = 1; 55} 56 57if($force == 1) 58{ 59 $targetObj->{force} = 1; 60} 61 62$targetObj->loadXML($serverwizFile); 63print "Loaded MRW XML: $serverwizFile \n"; 64 65# Iterate over Inventory and get all the Inventory targets. 66my @inventory = Inventory::getInventory($targetObj); 67for my $item (@inventory) 68{ 69 # Target to Obmc_Name hash. 70 $invHash{$item->{TARGET}} = $item->{OBMC_NAME}; 71} 72 73# For debugging purpose. 74printDebug("\nList of Inventory targets\n"); 75foreach my $key (sort keys %invHash) 76{ 77 printDebug("$invHash{$key}\n"); 78} 79 80# Process all the targets in the XML. If the target is associated with a FRU, 81# then remember it so that when we do the FRU inventory lookup, we know if 82# that Inventory has a LED associated with it or not. 83foreach my $target (sort keys %{$targetObj->getAllTargets()}) 84{ 85 # Some the target instances may *not* have this MRW_TYPE attribute. 86 if($targetObj->isBadAttribute($target, "MRW_TYPE")) 87 { 88 next; 89 } 90 91 # Return true if not populated -or- not present 92 if("LED" eq $targetObj->getMrwType($target)) 93 { 94 # Just for clarity. 95 my $ledTarget = $target; 96 97 # OBMC_NAME field of the FRU 98 # fruPath ex /system/chassis/motherboard/dimm1 99 # device "dimm1" 100 my $fruPath = ''; 101 my $device = ''; 102 103 # Find if this LED is associated with a FRU. 104 # Example, FAN will have LED on that assembly. 105 my $conns = $targetObj->findConnections($ledTarget, "LOGICAL_ASSOCIATION"); 106 if ($conns ne "") 107 { 108 # This LED is associated with a FRU 109 for my $conn (@{$conns->{CONN}}) 110 { 111 my $destTarget = $conn->{DEST_PARENT}; 112 # If we have found this, then that means, we do not need to 113 # hand cook a group name. delete this value from the inventory 114 # array 115 if(exists($invHash{$destTarget})) 116 { 117 # This will remove a particular {key, value} pair 118 $fruPath = $invHash{$destTarget}; 119 printDebug("$destTarget : $fruPath is having associated LED\n"); 120 delete ($invHash{$destTarget}); 121 } 122 } 123 # fetch FruName from the device path 124 $device = getFruName($fruPath); 125 printDebug("$target; $device has device\n"); 126 } 127 128 if($targetObj->isBadAttribute($ledTarget, "CONTROL_GROUPS")) 129 { 130 next; 131 } 132 133 # By default, Blink takes higher priority 134 my $priority = "'Blink'"; 135 136 # Get the priority. Since rest everything is populated, 137 # default to Blink than err'ing out. Not checking for 138 # validity of this since it must be present. 139 if($targetObj->getAttribute($ledTarget, "LED_PRIORITY") eq "ON") 140 { 141 $priority = "'On'"; 142 } 143 144 #The MRW instance name must match the LED name in the device tree 145 my $name = $targetObj->getInstanceName($ledTarget); 146 147 # Get if this LED is a ENC-FAULT type. 148 if(!$targetObj->isBadAttribute($target, "LED_TYPE")) 149 { 150 if("ENC-FAULT" eq $targetObj->getAttribute($ledTarget, "LED_TYPE")) 151 { 152 $encFaults{$name} = $priority; 153 } 154 } 155 156 # Defines the LEDs and the Groups that they belong to 157 my $controlGroup = $targetObj->getAttribute($ledTarget, "CONTROL_GROUPS"); 158 159 #remove spaces, because serverwiz isn't good at removing them itself 160 $controlGroup =~ s/\s//g; 161 my @groups= split(',', $controlGroup); #just a long 16x3 = 48 element list 162 163 for (my $i = 0; $i < scalar @groups; $i += 3) 164 { 165 if (($groups[$i] ne "NA") && ($groups[$i] ne "")) 166 { 167 my $groupName = $groups[$i]; 168 printDebug("$groupName\n"); 169 170 my $blinkFreq = $groups[$i+1]; 171 my $action = "'On'"; 172 my $period = 0; 173 174 # Period in milli seconds 175 my $dutyCycle = $groups[$i+2]; 176 if($blinkFreq > 0) 177 { 178 $action = "'Blink'"; 179 $period = (1 / $blinkFreq) * 1000; 180 } 181 182 # Insert into hash map; 183 $hashGroup{$groupName}{$name}{"Action"} = $action; 184 $hashGroup{$groupName}{$name}{"Period"} = $period; 185 $hashGroup{$groupName}{$name}{"DutyOn"} = $dutyCycle; 186 $hashGroup{$groupName}{$name}{"Priority"} = $priority; 187 188 # Need to update the LampTest group. 189 $hashGroup{$lampTest}{$name}{"Action"} = "'Blink'"; 190 $hashGroup{$lampTest}{$name}{"Period"} = 1000; 191 $hashGroup{$lampTest}{$name}{"DutyOn"} = 50; 192 193 # Priority of a particular LED needs to stay SAME across 194 # all groups 195 $hashGroup{$lampTest}{$name}{"Priority"} = $priority; 196 } 197 } # Walk CONTROL_GROUP 198 } # Has LED target 199} # All the targets 200 201 202# These are the FRUs that do not have associated LEDs. All of these need to be 203# mapped to some group, which will be named after this target name and the 204# elements of the group are EnclosureFaults Front and Back 205printDebug("\n======================================================================\n"); 206printDebug("\nFRUs that do not have associated LEDs\n"); 207foreach my $key (sort keys %invHash) 208{ 209 my $device = getFruName($invHash{$key}); 210 211 # For each of these device, the Group record would be this : 212 my $groupName = $device . "Fault"; 213 printDebug("$device :: $groupName\n"); 214 215 # Setup roll-up LEDs to the ones that are of type ENC-FAULT 216 foreach my $led (sort keys %encFaults) 217 { 218 $hashGroup{$groupName}{$led}{"Action"} = "'On'"; 219 $hashGroup{$groupName}{$led}{"Period"} = 0; 220 $hashGroup{$groupName}{$led}{"DutyOn"} = 50; 221 222 # Priority of a particular LED needs to stay SAME across 223 # all groups 224 $hashGroup{$groupName}{$led}{"Priority"} = $encFaults{$led}; 225 } 226} 227printDebug("\n======================================================================\n"); 228 229my $index = rindex($outputFile, "."); 230my $suffix = substr($outputFile, $index + 1); 231if (lc($suffix) eq "json") 232{ 233 # Generate the JSON file 234 generateJSONFile(); 235} 236else 237{ 238 # Generate the yaml file 239 generateYamlFile(); 240} 241 242#------------------------------------END OF MAIN----------------------- 243 244# Gven a '/' separated string, returns the leaf. 245# Ex: /a/b/c/d returns device=d 246sub getFruName 247{ 248 my $path = shift; 249 my $device = ''; 250 my $lastSlash=rindex($path, '/'); 251 $device=substr($path, $lastSlash+1); 252} 253 254sub generateYamlFile 255{ 256 my $fileName = $outputFile; 257 my $groupCopy = ''; 258 my $ledCopy = ''; 259 open(my $fh, '>', $fileName) or die "Could not open file '$fileName' $!"; 260 261 foreach my $group (sort keys %hashGroup) 262 { 263 if($group ne $groupCopy) 264 { 265 # If one of these is a default group, then delete it from the array 266 # that is being maintained to create one by hand if all default ones 267 # are not defined 268 my $index = first {$defaultGroup[$_] eq $group} 0..$#defaultGroup; 269 if (defined $index) 270 { 271 splice @defaultGroup, $index, 1; 272 } 273 274 $groupCopy = ''; 275 $ledCopy = ''; 276 } 277 278 foreach my $led (sort keys %{ $hashGroup{$group} }) 279 { 280 foreach my $property (sort keys %{ $hashGroup{$group}{$led}}) 281 { 282 if($group ne $groupCopy) 283 { 284 $groupCopy = $group; 285 print $fh "$group:\n"; 286 } 287 print $fh " "; 288 if($led ne $ledCopy) 289 { 290 $ledCopy = $led; 291 print $fh "$led:\n"; 292 print $fh " "; 293 } 294 print $fh " "; 295 print $fh "$property:"; 296 print $fh " $hashGroup{$group}{$led}{$property}\n"; 297 } 298 } 299 } 300 # If we need to hand create some of the groups, do so now. 301 foreach my $name (@defaultGroup) 302 { 303 print $fh "$name:\n"; 304 } 305 close $fh; 306} 307 308sub generateJSONFile 309{ 310 package LEDGroups; 311 use JSON; 312 my $JSON = JSON->new->utf8->pretty(1); 313 $JSON->convert_blessed(1); 314 315 sub led 316 { 317 my $class = shift; 318 my $self = { 319 group => shift, 320 members => shift, 321 }; 322 bless $self, $class; 323 return $self; 324 } 325 326 sub member 327 { 328 my $class = shift; 329 my $self = { 330 name => shift, 331 Action => shift, 332 DutyOn => shift, 333 Period => shift, 334 Priority => shift, 335 }; 336 bless $self, $class; 337 return $self; 338 } 339 340 sub TO_JSON { 341 return { %{ shift() } }; 342 } 343 344 my $fileName = $outputFile; 345 open(my $fh, '>', $fileName) or die "Could not open file '$fileName' $!"; 346 347 my @leds = (); 348 foreach my $group (sort keys %hashGroup) 349 { 350 my @members = (); 351 foreach my $led (sort keys %{ $hashGroup{$group} }) 352 { 353 my $action; 354 my $dutyOn; 355 my $period; 356 my $priority; 357 358 if (exists $hashGroup{$group}{$led}{Action}) 359 { 360 $action = $hashGroup{$group}{$led}{Action}; 361 $action = substr($action, 1, length($action) - 2); 362 } 363 364 if (exists $hashGroup{$group}{$led}{DutyOn}) 365 { 366 $dutyOn = $hashGroup{$group}{$led}{DutyOn}; 367 } 368 369 if (exists $hashGroup{$group}{$led}{Period}) 370 { 371 $period = $hashGroup{$group}{$led}{Period}; 372 } 373 374 if (exists $hashGroup{$group}{$led}{Priority}) 375 { 376 $priority = $hashGroup{$group}{$led}{Priority}; 377 $priority = substr($priority, 1, length($priority) - 2); 378 } 379 380 my $m = member LEDGroups($led, $action, $dutyOn, $period, $priority); 381 push @members, $m; 382 } 383 my $l = led LEDGroups($group, \@members); 384 push @leds, $l; 385 } 386 my %ledJson = ('leds' => \@leds); 387 my $json = $JSON->canonical(1)->encode(\%ledJson); 388 print $fh $json; 389 close $fh; 390} 391 392# Helper function to put debug statements. 393sub printDebug 394{ 395 my $str = shift; 396 print "DEBUG: ", $str, "\n" if $debug; 397} 398 399# Usage 400sub printUsage 401{ 402 print " 403 $0 -i [XML filename] -o [Output filename] [OPTIONS] 404Options: 405 -f = force output file creation even when errors 406 -d = debug mode 407 -v = verbose mode - for verbose o/p from Targets.pm 408 409PS: mrw::Targets can be found in https://github.com/open-power/serverwiz/ 410 mrw::Inventory can be found in https://github.com/openbmc/phosphor-mrw-tools/ 411 \n"; 412 exit(1); 413} 414#------------------------------------END OF SUB----------------------- 415