blob: 13c3df19d34dd9c49b8de8d9e58eafd6fc6cf099 [file] [log] [blame]
Matt Spinler45ae4042016-08-23 16:30:04 -05001#!/usr/bin/env perl
2
3#Creates the OpenBMC inventory from ServerWiz output XML.
4#Basically, the inventory includes anything with a FRU name,
5#plus some other specific things we always look for since we
6#need more than just FRUs.
7
8
9use strict;
10use XML::Simple;
11use mrw::Targets;
12use Getopt::Long;
13use JSON;
14
15my $serverwizFile;
16my $outputFile, my $target;
17my $fruName, my $type;
18my @items, my %item, my %inventory;
19
20#Ensure we never pick these up
21my %skipFRUTypes = (OCC => 1);
22
23#We always want the targets with these Types
24my %includedTypes = ("CORE" => 1);
25
26#We always want the targets with these MRW Types
27my %includedTargetTypes = ("chip-sp-bmc" => 1,
28 "chip-apss-psoc" => 1);
29
30#These are never considered FRUs
31my %notFRUTypes = ("CORE" => 1);
32
33GetOptions("x=s" => \$serverwizFile,
34 "o=s" => \$outputFile)
35or printUsage();
36
37if ((not defined $serverwizFile) || (not defined $outputFile)) {
38 printUsage();
39}
40
41my $targetObj = Targets->new;
42$targetObj->loadXML($serverwizFile);
43
44foreach $target (sort keys %{ $targetObj->getAllTargets() })
45{
46 $type = $targetObj->getType($target);
47 if (exists $skipFRUTypes{$type}) {
48 next;
49 }
50
51 $fruName = "";
52
53 if (!$targetObj->isBadAttribute($target, "FRU_NAME")) {
54 $fruName = $targetObj->getAttribute($target,"FRU_NAME");
55 }
56
57 my $targetType = $targetObj->getTargetType($target);
58
59 #We're looking for FRUs, and a few other required parts
60 if (($fruName ne "") || (exists $includedTargetTypes{$targetType}) ||
61 (exists $includedTypes{$type}))
62 {
63 $item{name} = $target;
64 $item{orig_name} = $target;
65 $item{fru_type} = $type;
66 $item{target_type} = $targetType;
67
68 if (($fruName ne "") && (not exists $notFRUTypes{$type})) {
69 $item{is_fru} = 1;
70 } else {
71 $item{is_fru} = 0;
72 }
73 push @items, { %item };
74 }
75
76}
77
Matt Spinler1e388122016-09-22 15:41:33 -050078#Hardcode the entries that will never be in the MRW
79#TODO: openbmc/openbmc#596 Remove when BIOS version is stored elsewhere.
80$inventory{'<inventory_root>/system/bios'} =
81 {is_fru => 1, fru_type => 'SYSTEM'};
82
83#TODO: openbmc/openbmc#597 Remove when misc FRU data is stored elsewhere.
84$inventory{'<inventory_root>/system/misc'} =
85 {is_fru => 0, fru_type => 'SYSTEM'};
86
Matt Spinler45ae4042016-08-23 16:30:04 -050087transform(\@items, \%inventory);
88
89#Encode in JSON and write it out
90my $json = JSON->new;
91$json->indent(1);
92$json->canonical(1);
93my $text = $json->encode(\%inventory);
94
95open(FILE, ">$outputFile") or die "Unable to create $outputFile\n";
96print FILE $text;
97close FILE;
98
99print "Created $outputFile\n";
100
101
102#Apply OpenBMC naming conventions to the Serverwiz names
103sub transform
104{
105 my $items = shift @_;
106 my $inventory = shift @_;
107
108 removeConnectors($items);
109
110 removeProcModule($items);
111
112 renameSegmentWithType("PROC", "cpu", $items);
113
114 renameSegmentWithType("SYS", "system", $items);
115 renameType("SYS", "SYSTEM", $items);
116
117 renameSegmentWithType("NODE", "chassis", $items);
118 renameType("NODE", "SYSTEM", $items);
119
120 renameSegmentWithTargetType("card-motherboard", "motherboard", $items);
121 renameTypeWithTargetType("card-motherboard", "MOTHERBOARD", $items);
122
123 renameType("FSP", "BMC", $items);
124
125 removeCoreParentChiplet($items);
126
127 removeInstNumIfOneInstPresent($items);
128
129 removeHyphensFromInstanceNum($items);
130
131 for my $i (@$items) {
132 my $name = "<inventory_root>".$i->{name};
133 delete $i->{name};
134 delete $i->{orig_name};
135 delete $i->{target_type};
136 $inventory{$name} = { %$i };
137 }
138}
139
140
141#Renames a segment in all target names based on its type
142#
143#For example:
144# renameSegmentWithType("PROC", "foo", $items)
145# would change
146# /sys-0/node-0/motherboard-0/module-0/cpu/core0
147# to
148# /sys-0/node-0/motherboard-0/module-0/foo/core0
149# assuming /sys-0/.../cpu had type PROC.
150sub renameSegmentWithType
151{
152 my $type = shift @_;
153 my $newSegment = shift @_;
154 my $items = shift @_;
155 my %segmentsToRename;
156
157 for my $item (@$items) {
158 my @segments = split('/', $item->{orig_name});
159 my $target = "";
160 for my $s (@segments) {
161 if (length($s) > 0) {
162 $target .= "/$s";
163 my $curType = "";
164 if (!$targetObj->isBadAttribute($target, "TYPE")) {
165 $curType = $targetObj->getType($target);
166 }
167 if ($curType eq $type) {
168 if (not defined $segmentsToRename{$target}) {
169 my ($oldSegment) = $target =~ /\b(\w+)(-\d+)?$/;
170 $segmentsToRename{$target}{old} = $oldSegment;
171 $segmentsToRename{$target}{new} = $newSegment;
172 }
173 }
174 }
175 }
176 }
177
178 for my $s (keys %segmentsToRename) {
179 for my $item (@$items) {
180 $item->{name} =~
181 s/$segmentsToRename{$s}{old}/$segmentsToRename{$s}{new}/;
182 }
183 }
184}
185
186
187#Renames a segment in all target names based on its target type
188#
189#For example:
190# renameSegmentWithType("PROC", "foo", $items)
191# would change
192# /sys-0/node-0/motherboard-0/module-0/cpu/core0
193# to
194# /sys-0/node-0/motherboard-0/module-0/foo/core0
195# assuming /sys-0/.../cpu had target type PROC.
196sub renameSegmentWithTargetType
197{
198 my $type = shift @_;
199 my $newSegment = shift @_;
200 my $items = shift @_;
201 my %segmentsToRename;
202
203 for my $item (@$items) {
204 my @segments = split('/', $item->{orig_name});
205 my $target = "";
206 for my $s (@segments) {
207 if (length($s) > 0) {
208 $target .= "/$s";
209 my $curType = $targetObj->getTargetType($target);
210 if ($curType eq $type) {
211 if (not defined $segmentsToRename{$target}) {
212 my ($oldSegment) = $target =~ /\b(\w+)(-\d+)?$/;
213 $segmentsToRename{$target}{old} = $oldSegment;
214 $segmentsToRename{$target}{new} = $newSegment;
215 }
216 }
217 }
218 }
219 }
220
221 for my $s (keys %segmentsToRename) {
222 for my $item (@$items) {
223 $item->{name} =~
224 s/$segmentsToRename{$s}{old}/$segmentsToRename{$s}{new}/;
225 }
226 }
227}
228
229
230#Remove the core's parent chiplet, after moving
231#the chiplet's instance number to the core.
232#Note: Serverwiz always puts the core on a chiplet
233sub removeCoreParentChiplet
234{
235 my $items = shift @_;
236
237 for my $item (@$items) {
238 if ($item->{fru_type} eq "CORE") {
239 $item->{name} =~ s/\w+-(\d+)\/(\w+)-\d+$/$2-$1/;
240 }
241 }
242}
243
244
245#Remove path segments that are connectors
246sub removeConnectors
247{
248 my $items = shift @_;
249 my %connectors;
250 my $item;
251
252 for $item (@$items) {
253 my @segments = split('/', $item->{name});
254 my $target = "";
255 for my $s (@segments) {
256 if (length($s) > 0) {
257 $target .= "/$s";
258 my $class = $targetObj->getAttribute($target, "CLASS");
259 if ($class eq "CONNECTOR") {
260 if (not exists $connectors{$target}) {
261 $connectors{$target} = 1;
262 }
263 }
264 }
265 }
266 }
267
268 #remove the connector segments out of the path
269 #Reverse sort so we start with connectors further out
270 for my $connector (sort {$b cmp $a} keys %connectors) {
271 for $item (@$items) {
272 if ($item->{name} =~ /$connector\b/) {
273 my ($inst) = $connector =~ /-(\d+)$/;
274 my ($card) = $item->{name};
275 $card =~ s/^$connector\///;
276
277 #add the connector instance to the child card
278 $card =~ s/^(\w+)-\d+/$1-$inst/;
279
280 #remove the connector segment from the path
281 my $base = $connector;
282 $base =~ s/\w+-\d+$//;
283 $item->{name} = $base . $card;
284 }
285 }
286 }
287}
288
289
290#Remove the processor module card from the path name.
291#Note: Serverwiz always outputs proc_socket-X/module-Y/proc.
292# where proc_socket, module, and proc can be any name
293# We already transormed it to module-X/proc.
294# Our use requires proc-X.
295# Note: No multichip modules in plan for OpenPower systems.
296sub removeProcModule
297{
298 my $items = shift @_;
299 my $procName = "";
300
301 #Find the name of the processor used in this model
302 for my $item (@$items) {
303 if ($item->{fru_type} eq "PROC") {
304 ($procName) = $item->{name} =~ /\b(\w+)$/;
305 last;
306 }
307 }
308
309 #Now remove it from every instance that it's a part of
310 if ($procName eq "") {
311 print "Could not find the name of the processor in this system\n";
312 } else {
313 for my $item (@$items) {
314 $item->{name} =~ s/\w+-(\d+)\/$procName/$procName-$1/;
315 }
316 }
317}
318
319
320sub renameType
321{
322 my $old = shift @_;
323 my $new = shift @_;
324 my $items = shift @_;
325
326 for my $item (@$items) {
327 $item->{fru_type} =~ s/$old/$new/;
328 }
329}
330
331
332sub renameTypeWithTargetType
333{
334 my $targetType = shift@_;
335 my $newType = shift @_;
336 my $items = shift @_;
337
338 for my $item (@$items) {
339 if ($item->{target_type} eq $targetType) {
340 $item->{fru_type} = $newType;
341 }
342 }
343}
344
345
346sub removeHyphensFromInstanceNum
347{
348 my $items = shift @_;
349
350 for my $item (@$items) {
351 $item->{name} =~ s/-(\d+)\b/$1/g;
352 }
353}
354
355
356sub renameSegment
357{
358 my $old = shift @_;
359 my $new = shift @_;
360 my $items = shift @_;
361
362 for my $item (@$items) {
363 $item->{name} =~ s/\b$old\b/$new/;
364 }
365}
366
367
368sub removeInstNumIfOneInstPresent
369{
370 my $items = shift @_;
371 my %instanceHash;
372 my $segment, my $item;
373
374 for $item (@$items) {
375 my @segments = split('/', $item->{name});
376
377 for $segment (@segments) {
378 my ($s, $inst) = $segment =~ /(\w+)-(\d+)/;
379 if (defined $s) {
380 if (not exists $instanceHash{$s}) {
381 $instanceHash{$s}{inst} = $inst;
382 }
383 else {
384 if ($instanceHash{$s}{inst} ne $inst) {
385 $instanceHash{$s}{keep} = 1;
386 }
387 }
388 }
389 }
390 }
391
392 for my $segment (keys %instanceHash) {
393
394 if (not exists $instanceHash{$segment}{keep}) {
395 for $item (@$items) {
396 $item->{name} =~ s/$segment-\d+/$segment/;
397 }
398 }
399 }
400}
401
402
403sub printUsage
404{
405 print "inventory.pl -x [XML filename] -o [output filename]\n";
406 exit(1);
407}