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