blob: b5d414495fc6448c0176c820fc0e3c1ed78e3ff5 [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 Spinlerf0942d12016-12-21 14:27:57 -0600141}
142
143
144#Removes non-FRU cards in the middle of a hierarchy from OBMC_NAME.
145#For example, .../motherboard/fanriser-0/fan-0 ->
146# .../motherboard/fan-0 when fanriser-0 isn't a FRU.
147sub removeNonFRUCardSegments
148{
149 my ($targetObj, $inventory) = @_;
150
151 for my $item (@$inventory) {
152
153 #Split the target into segments, then start
154 #adding segments in to make new targets so we can
155 #make API calls on the segment instances.
156 my @segments = split('/', $item->{TARGET});
157 my $target = "";
158 for my $s (@segments) {
159 next if (length($s) == 0);
160
161 $target .= "/$s";
162
163 my $class = $targetObj->getAttribute($target, "CLASS");
164 next if ($class ne "CARD");
165
166 my $ruType = $targetObj->getAttribute($target, "RU_TYPE");
167
168 #If this segment is a card but not a FRU,
169 #remove it from the path.
170 if (not exists $RU_TYPES{$ruType}) {
171 my $segment = $targetObj->getInstanceName($target);
172 $item->{OBMC_NAME} =~ s/\b$segment-\d+\b\///;
173 }
174 }
175 }
Matt Spinlerc119a902016-12-21 14:19:25 -0600176}
177
178
179#Removes connectors from the OBMC_NAME element. Also
180#takes the POSITION value of the connector and adds it
181#to the card segment that plugs into the connector.
182#For example:
183# /motherboard/card-conn-5/card-0 ->
184# /motherobard/card-5
185sub removeConnectors
186{
187 my ($targetObj, $inventory) = @_;
188
189 #Find the connectors embedded in the segments
190 for my $item (@$inventory) {
191
192 #Split the target into segments, then start
193 #adding segments in to make new targets
194 my @segments = split('/', $item->{TARGET});
195 my $target = "";
196 for my $s (@segments) {
197 next if (length($s) == 0);
198
199 $target .= "/$s";
200 my $class = $targetObj->getAttribute($target, "CLASS");
201 next unless ($class eq "CONNECTOR");
202
203 my ($segment) = $target =~ /\b(\w+-\d+)$/;
204 my $pos = $targetObj->getAttribute($target, "POSITION");
205
206 #change /connector-11/card-2/ to /card-11/
207 $item->{OBMC_NAME} =~ s/\b$segment\/(\w+)-\d+/$1-$pos/;
208
209 }
210 }
211}
212
Matt Spinler8fd594c2016-12-21 14:22:09 -0600213
Matt Spinlereff6a192016-12-21 15:09:39 -0600214#Units, typically cores, can be subunits of other subunits of
215#their parent chip. We can remove all of these intermediate
216#units. For example, cpu0/someunit1/someunit2/core3 ->
217#cpu0/core3.
218sub removeIntermediateUnits
219{
220 my ($targetObj, $inventory) = @_;
221
222 for my $item (@$inventory) {
223
224 my $class = $targetObj->getAttribute($item->{TARGET}, "CLASS");
225 next unless ($class eq "UNIT");
226
227 my $parent = $targetObj->getTargetParent($item->{TARGET});
228
229 #Remove all of these intermediate units until we find
230 #something that isn't a unit (most likely a chip).
231 while ($targetObj->getAttribute($parent, "CLASS") eq "UNIT") {
232
233 my $name = $targetObj->getInstanceName($parent);
234 $item->{OBMC_NAME} =~ s/$name(-)*(\d+)*\///;
235
236 $parent = $targetObj->getTargetParent($parent);
237 }
238 }
239}
240
241
Matt Spinlercf323822016-12-21 14:31:08 -0600242#Renames segments of the paths to the name passed in
243#based on the type of the segment.
244#For example:
245# renameSegmentWithType("PROC", "cpu", ...);
246# With a target of:
247# motherboard-0/myp9proc-5/core-3
248# Where target motherboard-0/myp9proc-5 has a type
249# of PROC, gives:
250# motherboard-0/cpu-5/core-3
251sub renameSegmentWithType
252{
253 my ($type, $newSegment, $targetObj, $inventory) = @_;
254
255 #Find the targets for all the segments, and
256 #if their type matches what we're looking for, then
257 #save it so we can rename them later.
258 for my $item (@$inventory) {
259
260 my @segments = split('/', $item->{TARGET});
261 my $target = "";
262
263 for my $s (@segments) {
264 next if (length($s) == 0);
265
266 $target .= "/$s";
267 my $curType = $targetObj->getType($target);
268 next unless ($curType eq $type);
269
270 my $oldSegment = $targetObj->getInstanceName($target);
271 $item->{OBMC_NAME} =~ s/$oldSegment/$newSegment/;
272 }
273 }
274}
275
276
Matt Spinler8fd594c2016-12-21 14:22:09 -0600277#Removes the card portion of a module from OBMC_NAME.
278#For example, .../motherboard-0/module-1/proc-0 ->
279#.../motherboard-0/proc-1.
280#This needs to be revisited if multi-processor modules
281#ever come into plan.
282sub removeModuleFromPath
283{
284 my ($targetObj, $inventory) = @_;
285 my %chipNames;
286
287 #Find the names of the chips on the modules
288 for my $item (@$inventory) {
289 if (exists $MODULE_TYPES{$targetObj->getType($item->{TARGET})}) {
290 $chipNames{$targetObj->getInstanceName($item->{TARGET})} = 1;
291 }
292 }
293
294 #Now convert module-A/name-B to name-A
295 #Note that the -B isn't always present
296 for my $item (@$inventory) {
297
298 for my $name (keys %chipNames) {
299 $item->{OBMC_NAME} =~ s/\w+-(\d+)\/$name(-\d+)*/$name-$1/;
300 }
301 }
302}
303
Matt Spinlercf323822016-12-21 14:31:08 -0600304
305#The same as renameSegmentWithType, but finds the segment
306#to rename by calling Targets::getTargetType() on it
307#instead of Targets::getType().
308sub renameSegmentWithTargetType
309{
310 my ($type, $newSegment, $targetObj, $inventory) = @_;
311
312 for my $item (@$inventory) {
313
314 my @segments = split('/', $item->{TARGET});
315 my $target = "";
316
317 for my $s (@segments) {
318 next if (length($s) == 0);
319
320 $target .= "/$s";
321 my $curType = $targetObj->getTargetType($target);
322 next unless ($curType eq $type);
323
324 my $oldSegment = $targetObj->getInstanceName($target);
325 $item->{OBMC_NAME} =~ s/$oldSegment/$newSegment/;
326 }
327 }
328}
329
Matt Spinler306e4d12016-12-21 14:33:31 -0600330
331#Removes the instance number from the OBMC_NAME segments
332#where only 1 of those segments exists because numbering isn't
333#necessary to distinguish them.
334sub removeInstNumIfOneInstPresent
335{
336 my ($inventory) = @_;
337 my %instanceHash;
338
339 for my $item (@$inventory) {
340 #Look at all the segments, keeping track if we've
341 #seen a particular segment with the same instance before.
342 my @segments = split('/', $item->{OBMC_NAME});
343 for my $segment (@segments) {
344 my ($s, $inst) = $segment =~ /(\w+)-(\d+)/;
345 if (defined $s) {
346 if (not exists $instanceHash{$s}) {
347 $instanceHash{$s}{inst} = $inst;
348 }
349 else {
350 if ($instanceHash{$s}{inst} ne $inst) {
351 $instanceHash{$s}{keep} = 1;
352 }
353 }
354 }
355 }
356 }
357
358 #Remove the instanc numbers we don't need to keep.
359 for my $segment (keys %instanceHash) {
360 if (not exists $instanceHash{$segment}{keep}) {
361 for my $item (@$inventory) {
362 $item->{OBMC_NAME} =~ s/$segment-\d+/$segment/;
363 }
364 }
365 }
366}
367
368
369#Removes the '-' from between the segment name and instance.
370sub removeHyphensFromInstanceNum
371{
372 my ($inventory) = @_;
373
374 for my $item (@$inventory) {
375 $item->{OBMC_NAME} =~ s/-(\d+)\b/$1/g;
376 }
377}
378
Matt Spinlerbfd10b12016-12-09 10:16:54 -06003791;
380
381=head1 NAME
382
383Inventory
384
385=head1 DESCRIPTION
386
387Retrieves the OpenBMC inventory from the MRW.
388
389The inventory contains:
390
391=over 4
392
393=item * The system target
394
395=item * The chassis target(s) (Called a 'node' in the MRW.)
396
397=item * All targets of class CARD or CHIP that are FRUs.
398
399=item * All targets of type PROC
400
Matt Spinlereff6a192016-12-21 15:09:39 -0600401=item * All targets of type CORE
402
Matt Spinlerbfd10b12016-12-09 10:16:54 -0600403=item * All targets of type BMC
404
405=item * All targets of type GPU
406
407=back
408
409=head2 Notes:
410
411The processor and GPU chips are usually modeled in the MRW as a
412card->chip package that would plug into a connector on the motherboard
413or other parent card. So, even if both card and chip are marked as a FRU,
414there will only be 1 entry in the inventory for both, and the MRW
415target associated with it will be for the chip and not the card.
416
417In addition, this intermediate card will be removed from the path name:
418 /system/chassis/motheboard/cpu and not
419 /system/chassis/motherboard/cpucard/cpu
420
421=head2 Inventory naming conventions
422
423The inventory names returned in the OBMC_NAME hash element will follow
424the conventions listed below. An example of an inventory name is:
425/system/chassis/motherboard/cpu5
426
427=over 4
428
429=item * If there is only 1 instance of any segment in the system, then
430 it won't have an instance number, otherwise there will be one.
431
432=item * The root of the name is '/system'.
433
434=item * After system is 'chassis', of which there can be 1 or more.
435
436=item * The name is based on the MRW card plugging order, and not what
437 the system looks like from the outside. For example, a power
438 supply that plugs into a motherboard (maybe via a non-fru riser
439 or cable, see the item below), would be:
440 /system/chassis/motherboard/psu2 and not
441 /system/chassis/psu2.
442
443=item * If a card is not a FRU so isn't in the inventory itself, then it
444 won't show up in the name of any child cards that are FRUs.
445 For example, if fan-riser isn't a FRU, it would be
446 /system/chassis/motherboard/fan3 and not
447 /system/chassis/motherboard/fan-riser/fan3.
448
449=item * The MRW models connectors between cards, but these never show up
450 in the inventory name.
451
452=item * If there is a motherboard, it is always called 'motherboard'.
453
454=item * Processors, GPUs, and BMCs are always called: 'cpu', 'gpu' and
455 'bmc' respectively.
456
457=back
458
459=head1 METHODS
460
461=over 4
462
463=item getInventory (C<TargetsObj>)
464
465Returns an array of hashes representing inventory items.
466
467The Hash contains:
468
469* TARGET: The MRW target of the item, for example:
470
471 /sys-0/node-0/motherboard-0/proc_socket-0/module-0/p9_proc_m
472
473* OBMC_NAME: The OpenBMC name of the item, for example:
474
475 /system/chassis/motherboard/cpu2
476
477=back
478
479=cut