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