blob: cc8c63a919b4b9bfc8ad3974fbf65a36215c499b [file] [log] [blame]
Matt Spinlerbfd10b12016-12-09 10:16:54 -06001package Inventory;
2
3use strict;
Matt Spinler39a033b2016-12-21 14:08:59 -06004use warnings;
Matt Spinlerbfd10b12016-12-09 10:16:54 -06005
Matt Spinler39a033b2016-12-21 14:08:59 -06006#Target types to always include in the inventory if present
Matt Spinler123437f2017-08-08 09:54:34 -05007my %TYPES = (SYS => 1, NODE => 1, PROC => 1,
8 BMC => 1, GPU => 1, CORE => 1, OCC => 1);
Matt Spinler39a033b2016-12-21 14:08:59 -06009
10#RU_TYPES of cards to include
11#FRU = field replaceable unit, CRU = customer replaceable unit
12my %RU_TYPES = (FRU => 1, CRU => 1);
13
Matt Spinlerfe759642016-12-21 14:13:59 -060014#Chips that are modeled as modules (card-chip together)
15my %MODULE_TYPES = (PROC => 1, GPU => 1);
16
Matt Spinler39a033b2016-12-21 14:08:59 -060017#Returns an array of hashes that represents the inventory
18#for a system. The hash elements are:
19#TARGET: The MRW target of the item
20#OBMC_NAME: The OpenBMC name for the item. This is usually
21# a simplified version of the target.
Matt Spinlerbfd10b12016-12-09 10:16:54 -060022sub getInventory
23{
24 my $targetObj = shift;
25 my @inventory;
26
Matt Spinler39a033b2016-12-21 14:08:59 -060027 if (ref($targetObj) ne "Targets") {
28 die "Invalid Targets object passed to getInventory\n";
29 }
30
31 findItems($targetObj, \@inventory);
32
Matt Spinlerfe759642016-12-21 14:13:59 -060033 pruneModuleCards($targetObj, \@inventory);
34
Matt Spinlerc119a902016-12-21 14:19:25 -060035 makeOBMCNames($targetObj, \@inventory);
36
Matt Spinlerbfd10b12016-12-09 10:16:54 -060037 return @inventory;
38}
39
Matt Spinler39a033b2016-12-21 14:08:59 -060040
41#Finds the inventory targets in the MRW.
42#It selects them if the target's type is in %TYPES
43#or the target's RU_TYPE is in %RU_TYPES.
44#This will pick up FRUs and other chips like the BMC and processor.
45sub findItems
46{
47 my ($targetObj, $inventory) = @_;
48
49 for my $target (sort keys %{$targetObj->getAllTargets()}) {
50 my $type = "";
51 my $ruType = "";
52
53 if (!$targetObj->isBadAttribute($target, "TYPE")) {
54 $type = $targetObj->getAttribute($target, "TYPE");
55 }
56
57 if (!$targetObj->isBadAttribute($target, "RU_TYPE")) {
58 $ruType = $targetObj->getAttribute($target, "RU_TYPE");
59 }
60
61 if ((exists $TYPES{$type}) || (exists $RU_TYPES{$ruType})) {
62 my %item;
63 $item{TARGET} = $target;
64 $item{OBMC_NAME} = $target; #Will fixup later
65 push @$inventory, { %item };
66 }
67 }
68}
69
70
Matt Spinlerfe759642016-12-21 14:13:59 -060071#Removes entries from the inventory for the card target of a module.
72#Needed because processors and GPUs are modeled as a package which
73#is a card-chip instance that plugs into a connector on the
74#backplane/processor card. Since we already include the chip target
75#in the inventory (that's how we can identify what it is), we don't
76#need the entry for the card target.
77#
78#For example, we'll already have .../module-0/proc-0 so we don't
79#need a separate .../module-0 entry.
80sub pruneModuleCards
81{
82 my ($targetObj, $inventory) = @_;
83 my @toRemove;
84
85 #Find the parent (a card) of items of type %type
86 for my $item (@$inventory) {
87
88 if (exists $MODULE_TYPES{$targetObj->getType($item->{TARGET})}) {
89 my $card = $targetObj->getTargetParent($item->{TARGET});
90 push @toRemove, $card;
91 }
92 }
93
94 #Remove these parent cards
95 for my $c (@toRemove) {
96 for my $i (0 .. (scalar @$inventory) - 1) {
97 if ($c eq $inventory->[$i]{TARGET}) {
98 splice(@$inventory, $i, 1);
99 last;
100 }
101 }
102 }
103}
104
Matt Spinlerc119a902016-12-21 14:19:25 -0600105
106#Makes the OpenBMC name for the targets in the inventory.
107#Removes unnecessary segments of the path name, renames
108#some segments to match standard conventions, and numbers
109#segments based on their position attribute.
110sub makeOBMCNames
111{
112 my ($targetObj, $inventory) = @_;
113
114 #Remove connector segments from the OBMC_NAME
115 removeConnectors($targetObj, $inventory);
116
Matt Spinler8fd594c2016-12-21 14:22:09 -0600117 #Don't need the card instance of a PROC/GPU module
118 removeModuleFromPath($targetObj, $inventory);
Matt Spinlerf0942d12016-12-21 14:27:57 -0600119
120 #Don't need card segments for non-FRUs
121 removeNonFRUCardSegments($targetObj, $inventory);
Matt Spinlercf323822016-12-21 14:31:08 -0600122
Matt Spinlereff6a192016-12-21 15:09:39 -0600123 #Don't need to show the middle units between proc & core
124 removeIntermediateUnits($targetObj, $inventory);
125
Matt Spinlercf323822016-12-21 14:31:08 -0600126 #Certain parts have standard naming
127 renameSegmentWithType("PROC", "cpu", $targetObj, $inventory);
128 renameSegmentWithType("SYS", "system", $targetObj, $inventory);
129 renameSegmentWithType("NODE", "chassis", $targetObj, $inventory);
130
131 #Make sure the motherboard is called 'motherboard' in the OBMC_NAME.
132 #The only way to identify it is with its TARGET_TYPE,
133 #which is different than the regular type.
134 renameSegmentWithTargetType("card-motherboard", "motherboard",
135 $targetObj, $inventory);
Matt Spinler306e4d12016-12-21 14:33:31 -0600136
137 #Don't need instance numbers unless there are more than 1 present
138 removeInstNumIfOneInstPresent($inventory);
139
140 #We want card1, not card-1
141 removeHyphensFromInstanceNum($inventory);
Matt Spinler3c784b62017-02-15 13:19:43 -0600142
143 pointChassisAtMotherboard($targetObj, $inventory);
Matt Spinlerf0942d12016-12-21 14:27:57 -0600144}
145
146
147#Removes non-FRU cards in the middle of a hierarchy from OBMC_NAME.
148#For example, .../motherboard/fanriser-0/fan-0 ->
149# .../motherboard/fan-0 when fanriser-0 isn't a FRU.
150sub removeNonFRUCardSegments
151{
152 my ($targetObj, $inventory) = @_;
153
154 for my $item (@$inventory) {
155
156 #Split the target into segments, then start
157 #adding segments in to make new targets so we can
158 #make API calls on the segment instances.
159 my @segments = split('/', $item->{TARGET});
160 my $target = "";
161 for my $s (@segments) {
162 next if (length($s) == 0);
163
164 $target .= "/$s";
165
166 my $class = $targetObj->getAttribute($target, "CLASS");
167 next if ($class ne "CARD");
168
169 my $ruType = $targetObj->getAttribute($target, "RU_TYPE");
170
171 #If this segment is a card but not a FRU,
172 #remove it from the path.
173 if (not exists $RU_TYPES{$ruType}) {
174 my $segment = $targetObj->getInstanceName($target);
175 $item->{OBMC_NAME} =~ s/\b$segment-\d+\b\///;
176 }
177 }
178 }
Matt Spinlerc119a902016-12-21 14:19:25 -0600179}
180
181
182#Removes connectors from the OBMC_NAME element. Also
183#takes the POSITION value of the connector and adds it
184#to the card segment that plugs into the connector.
185#For example:
186# /motherboard/card-conn-5/card-0 ->
187# /motherobard/card-5
188sub removeConnectors
189{
190 my ($targetObj, $inventory) = @_;
191
192 #Find the connectors embedded in the segments
193 for my $item (@$inventory) {
194
195 #Split the target into segments, then start
196 #adding segments in to make new targets
197 my @segments = split('/', $item->{TARGET});
198 my $target = "";
199 for my $s (@segments) {
200 next if (length($s) == 0);
201
202 $target .= "/$s";
203 my $class = $targetObj->getAttribute($target, "CLASS");
204 next unless ($class eq "CONNECTOR");
205
206 my ($segment) = $target =~ /\b(\w+-\d+)$/;
207 my $pos = $targetObj->getAttribute($target, "POSITION");
208
209 #change /connector-11/card-2/ to /card-11/
210 $item->{OBMC_NAME} =~ s/\b$segment\/(\w+)-\d+/$1-$pos/;
211
212 }
213 }
214}
215
Matt Spinler8fd594c2016-12-21 14:22:09 -0600216
Matt Spinlereff6a192016-12-21 15:09:39 -0600217#Units, typically cores, can be subunits of other subunits of
218#their parent chip. We can remove all of these intermediate
219#units. For example, cpu0/someunit1/someunit2/core3 ->
220#cpu0/core3.
221sub removeIntermediateUnits
222{
223 my ($targetObj, $inventory) = @_;
224
225 for my $item (@$inventory) {
226
227 my $class = $targetObj->getAttribute($item->{TARGET}, "CLASS");
228 next unless ($class eq "UNIT");
229
230 my $parent = $targetObj->getTargetParent($item->{TARGET});
231
232 #Remove all of these intermediate units until we find
233 #something that isn't a unit (most likely a chip).
234 while ($targetObj->getAttribute($parent, "CLASS") eq "UNIT") {
235
236 my $name = $targetObj->getInstanceName($parent);
237 $item->{OBMC_NAME} =~ s/$name(-)*(\d+)*\///;
238
239 $parent = $targetObj->getTargetParent($parent);
240 }
241 }
242}
243
244
Matt Spinlercf323822016-12-21 14:31:08 -0600245#Renames segments of the paths to the name passed in
246#based on the type of the segment.
247#For example:
248# renameSegmentWithType("PROC", "cpu", ...);
249# With a target of:
250# motherboard-0/myp9proc-5/core-3
251# Where target motherboard-0/myp9proc-5 has a type
252# of PROC, gives:
253# motherboard-0/cpu-5/core-3
254sub renameSegmentWithType
255{
256 my ($type, $newSegment, $targetObj, $inventory) = @_;
257
258 #Find the targets for all the segments, and
259 #if their type matches what we're looking for, then
260 #save it so we can rename them later.
261 for my $item (@$inventory) {
262
263 my @segments = split('/', $item->{TARGET});
264 my $target = "";
265
266 for my $s (@segments) {
267 next if (length($s) == 0);
268
269 $target .= "/$s";
270 my $curType = $targetObj->getType($target);
271 next unless ($curType eq $type);
272
273 my $oldSegment = $targetObj->getInstanceName($target);
274 $item->{OBMC_NAME} =~ s/$oldSegment/$newSegment/;
275 }
276 }
277}
278
279
Matt Spinler8fd594c2016-12-21 14:22:09 -0600280#Removes the card portion of a module from OBMC_NAME.
281#For example, .../motherboard-0/module-1/proc-0 ->
282#.../motherboard-0/proc-1.
283#This needs to be revisited if multi-processor modules
284#ever come into plan.
285sub removeModuleFromPath
286{
287 my ($targetObj, $inventory) = @_;
288 my %chipNames;
289
290 #Find the names of the chips on the modules
291 for my $item (@$inventory) {
292 if (exists $MODULE_TYPES{$targetObj->getType($item->{TARGET})}) {
293 $chipNames{$targetObj->getInstanceName($item->{TARGET})} = 1;
294 }
295 }
296
297 #Now convert module-A/name-B to name-A
298 #Note that the -B isn't always present
299 for my $item (@$inventory) {
300
301 for my $name (keys %chipNames) {
302 $item->{OBMC_NAME} =~ s/\w+-(\d+)\/$name(-\d+)*/$name-$1/;
303 }
304 }
305}
306
Matt Spinlercf323822016-12-21 14:31:08 -0600307
308#The same as renameSegmentWithType, but finds the segment
309#to rename by calling Targets::getTargetType() on it
310#instead of Targets::getType().
311sub renameSegmentWithTargetType
312{
313 my ($type, $newSegment, $targetObj, $inventory) = @_;
314
315 for my $item (@$inventory) {
316
317 my @segments = split('/', $item->{TARGET});
318 my $target = "";
319
320 for my $s (@segments) {
321 next if (length($s) == 0);
322
323 $target .= "/$s";
324 my $curType = $targetObj->getTargetType($target);
325 next unless ($curType eq $type);
326
327 my $oldSegment = $targetObj->getInstanceName($target);
328 $item->{OBMC_NAME} =~ s/$oldSegment/$newSegment/;
329 }
330 }
331}
332
Matt Spinler306e4d12016-12-21 14:33:31 -0600333
Matt Spinler3c784b62017-02-15 13:19:43 -0600334#FRU management code needs the node/chassis item to point
335#to the motherboard and not /system/chassis. Can revisit this
336#for multi-chassis systems if they ever show up.
337sub pointChassisAtMotherboard
338{
339 my ($targetObj, $inventory) = @_;
340 my $newName = undef;
341
342 for my $item (@$inventory) {
343 my $type = $targetObj->getTargetType($item->{TARGET});
344 if ($type eq "card-motherboard") {
345 $newName = $item->{OBMC_NAME};
346 last;
347 }
348 }
349
350 for my $item (@$inventory) {
351 if ($targetObj->getType($item->{TARGET}) eq "NODE") {
352 if (defined $newName) {
353 $item->{OBMC_NAME} = $newName;
354 }
355 last;
356 }
357 }
358}
359
360
Matt Spinler306e4d12016-12-21 14:33:31 -0600361#Removes the instance number from the OBMC_NAME segments
362#where only 1 of those segments exists because numbering isn't
363#necessary to distinguish them.
364sub removeInstNumIfOneInstPresent
365{
366 my ($inventory) = @_;
367 my %instanceHash;
368
369 for my $item (@$inventory) {
370 #Look at all the segments, keeping track if we've
371 #seen a particular segment with the same instance before.
372 my @segments = split('/', $item->{OBMC_NAME});
373 for my $segment (@segments) {
374 my ($s, $inst) = $segment =~ /(\w+)-(\d+)/;
375 if (defined $s) {
376 if (not exists $instanceHash{$s}) {
377 $instanceHash{$s}{inst} = $inst;
378 }
379 else {
380 if ($instanceHash{$s}{inst} ne $inst) {
381 $instanceHash{$s}{keep} = 1;
382 }
383 }
384 }
385 }
386 }
387
388 #Remove the instanc numbers we don't need to keep.
389 for my $segment (keys %instanceHash) {
390 if (not exists $instanceHash{$segment}{keep}) {
391 for my $item (@$inventory) {
392 $item->{OBMC_NAME} =~ s/$segment-\d+/$segment/;
393 }
394 }
395 }
396}
397
398
399#Removes the '-' from between the segment name and instance.
400sub removeHyphensFromInstanceNum
401{
402 my ($inventory) = @_;
403
404 for my $item (@$inventory) {
405 $item->{OBMC_NAME} =~ s/-(\d+)\b/$1/g;
406 }
407}
408
Matt Spinlerbfd10b12016-12-09 10:16:54 -06004091;
410
411=head1 NAME
412
413Inventory
414
415=head1 DESCRIPTION
416
417Retrieves the OpenBMC inventory from the MRW.
418
419The inventory contains:
420
421=over 4
422
423=item * The system target
424
425=item * The chassis target(s) (Called a 'node' in the MRW.)
426
427=item * All targets of class CARD or CHIP that are FRUs.
428
429=item * All targets of type PROC
430
Matt Spinlereff6a192016-12-21 15:09:39 -0600431=item * All targets of type CORE
432
Matt Spinlerbfd10b12016-12-09 10:16:54 -0600433=item * All targets of type BMC
434
435=item * All targets of type GPU
436
437=back
438
439=head2 Notes:
440
441The processor and GPU chips are usually modeled in the MRW as a
442card->chip package that would plug into a connector on the motherboard
443or other parent card. So, even if both card and chip are marked as a FRU,
444there will only be 1 entry in the inventory for both, and the MRW
445target associated with it will be for the chip and not the card.
446
447In addition, this intermediate card will be removed from the path name:
448 /system/chassis/motheboard/cpu and not
449 /system/chassis/motherboard/cpucard/cpu
450
451=head2 Inventory naming conventions
452
453The inventory names returned in the OBMC_NAME hash element will follow
454the conventions listed below. An example of an inventory name is:
455/system/chassis/motherboard/cpu5
456
457=over 4
458
459=item * If there is only 1 instance of any segment in the system, then
460 it won't have an instance number, otherwise there will be one.
461
462=item * The root of the name is '/system'.
463
464=item * After system is 'chassis', of which there can be 1 or more.
465
466=item * The name is based on the MRW card plugging order, and not what
467 the system looks like from the outside. For example, a power
468 supply that plugs into a motherboard (maybe via a non-fru riser
469 or cable, see the item below), would be:
470 /system/chassis/motherboard/psu2 and not
471 /system/chassis/psu2.
472
473=item * If a card is not a FRU so isn't in the inventory itself, then it
474 won't show up in the name of any child cards that are FRUs.
475 For example, if fan-riser isn't a FRU, it would be
476 /system/chassis/motherboard/fan3 and not
477 /system/chassis/motherboard/fan-riser/fan3.
478
479=item * The MRW models connectors between cards, but these never show up
480 in the inventory name.
481
482=item * If there is a motherboard, it is always called 'motherboard'.
483
484=item * Processors, GPUs, and BMCs are always called: 'cpu', 'gpu' and
485 'bmc' respectively.
486
487=back
488
489=head1 METHODS
490
491=over 4
492
493=item getInventory (C<TargetsObj>)
494
495Returns an array of hashes representing inventory items.
496
497The Hash contains:
498
499* TARGET: The MRW target of the item, for example:
500
501 /sys-0/node-0/motherboard-0/proc_socket-0/module-0/p9_proc_m
502
503* OBMC_NAME: The OpenBMC name of the item, for example:
504
505 /system/chassis/motherboard/cpu2
506
507=back
508
509=cut