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