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