-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshow_vio_host_mappings
executable file
·579 lines (519 loc) · 19.5 KB
/
show_vio_host_mappings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
#!/usr/bin/perl
#
# Shows virtual controller (vhost and vfchost) mappings in a VIOS environment
#
# Armin Kunaschik
# Version 1.0 initial release
# Version 1.1 added WWPN of VIO HBA (because SAN guys requested it)
# Version 1.2 removed unnecessary loop that caused bad performance
# Version 1.3 2015-04-30 cosmetic changes, slot numbers are left justified now, ioscli APAR mentioned
# Version 1.4 2020-11-07 added code to read luninfo.cfg, currently not used
# Version 1.5 fixed more trimming issues, added client WWPNs from fcstat -client
#
# Bugs: Script might display garbage for LPAR and/or LPARID when executed while a lpar is booting
# there is no documentation on all possible kdb output :-(
#
# Workaround: Don't execute while lpars are booting :-)
#
use strict;
use warnings;
use 5.010; # because of //
use Getopt::Long;
use XML::Simple; # for config file
#use Data::Dumper;
my $cfgfile="/etc/luninfo.cfg";
my $cfg; #configuration hash
my %options = ( 'disks' => 0,
'noheader' => 0,
'help' => 0
);
sub usage {
print STDERR "Usage: $0 [-h] | [-H ] | [ -d ]\n";
print STDERR " display vhost to LPAR mapping\n";
print STDERR " -h display this help\n";
print STDERR " -d display vhost to LPAR mapping with mapped disks and vtds (vscsi only)\n";
print STDERR " -H don't display header line\n";
exit 1;
}
Getopt::Long::Configure ("bundling"); # enable bundling of options like -lp same as -l -p
GetOptions( "d" => \$options{'disks'},
"H" => \$options{'noheader'},
"h" => \$options{'help'},
) or usage;
# any option list containing -h will display usage
if ( $options{'help'} ) { usage; };
my %locations;
if ( -e $cfgfile ) {
$cfg=XMLin($cfgfile, ForceArray => 1);
}
foreach my $location ( keys %{$cfg->{'location'}} ) {
foreach my $element ( keys %{$cfg->{'location'}->{$location}} ) {
for ( $element) {
if ( /storage/ ) {
foreach my $box ( keys %{$cfg->{'location'}->{$location}->{$element}} ) {
if ( defined $locations{$box} ) {
print STDERR "ERROR in $cfgfile: Duplicate use of storage serial $box! Using first definition.\n";
next;
}
$locations{$box}=$location;
}
}
}
}
}
#print Data::Dumper->Dump([\%locations]);
# hashes for vscsi, vfc and physical FC adapters
my %vscsi;
my %vfc;
my %adapters;
# disks hash holds hdisk-vtd mappings and other storage info (location, box, uuid etc)
my %disks;
# get list of virtual adapters, status and physical locations
open(CMD,"/usr/sbin/lsdev -c adapter -t IBM,v-scsi-host -F'name status physloc'|") or die "Error running lsdev -c adapter -t IBM,v-scsi-host -F'name status physloc'!";
while(<CMD>) {
chomp;
my ($name, $status, $physloc)=split;
$vscsi{$name}->{"name"}=$name;
$vscsi{$name}->{"status"}=$status;
$vscsi{$name}->{"type"}='v-scsi-host';
$vscsi{$name}->{"physloc"}=$physloc;
if ( $physloc =~ /[^-]+\-V[0-9]+\-(C[0-9]+)/ ) {
$vscsi{$name}->{"slot"}=$1;
}
}
close(CMD);
open(CMD,"/usr/sbin/lsdev -c adapter -t IBM,vfc-server -F'name status physloc'|") or die "Error running lsdev -c adapter -t IBM,vfc-server -F'name status physloc'!";
while(<CMD>) {
chomp;
my ($name, $status, $physloc)=split;
$vfc{$name}->{"name"}=$name;
$vfc{$name}->{"status"}=$status;
$vfc{$name}->{"type"}='vfc-server';
$vfc{$name}->{"physloc"}=$physloc;
if ( $physloc =~ /[^-]+\-V[0-9]+\-(C[0-9]+)/ ) {
$vfc{$name}->{"slot"}=$1;
}
}
close(CMD);
open(CMD,'/usr/sbin/lsdev -Cc adapter -F name:status:type|') or die "Error running /usr/sbin/lsdev -Cc adapter -F name:status:type !\n";
while (<CMD>) {
chomp;
next if ! /^fcs/; # only physical fcs adapters, more to include here?
my ($adapter,$status,$type)=split(/:/);
$adapters{$adapter}->{'status'}=$status;
$adapters{$adapter}->{'name'}=$adapter;
$adapters{$adapter}->{'type'}=$type;
if ( $adapter =~ /^fcs/ ) {
open(CMD1,"/usr/sbin/lscfg -vl $adapter|") or die "Error running /usr/sbin/lscfg -vl $adapter !\n";
while(<CMD1>) {
# get WWPN of physical adapter, easy way, lscfg works only for physical adapters
if ( /\s+Network Address\.+\s*(\S+)/ ) { $adapters{$adapter}->{'wwpn'}=$1; }
}
close(CMD1);
}
}
close(CMD);
# get mapped adapter name like this:
# lsattr -El vfchost0 -a map_port
# map_port fcs1 Physical FC Port False
# BEWARE: the man page states, that the field order is not always followed in the output!
# ioscli lsmap -all -npiv -fmt , -field name clntname status fc clntid physloc vfcclient vfcclientdrc
# vfchost0,server1,52,U9117.MMC.0601234-V6-C11,LOGGED_IN,fcs1,fcs3,U9117.MMC.0601234-V52-C17
# vfchost1,server1,52,U9117.MMC.0601234-V6-C10,LOGGED_IN,fcs2,fcs2,U9117.MMC.0601234-V52-C16
# vfchost0, ,27,U9117.MMC.0601234-V3-C10,NOT_LOGGED_IN,fcs0, ,
# in the future, IV72745 might fix some of this... no, it didn't.
# Wow! IV86769 did not fix lsmap -fmt but added a new option -fmt2... just for me(!), and it took only a few years
# TODO: Change code to use -fmt2 when there is no VIOS 2 anymore
open(CMD,"cd /tmp; /usr/ios/cli/ioscli lsmap -all -npiv -fmt , -field name clntname status fc clntid physloc vfcclient vfcclientdrc|") or die "Error running ioscli command\n";
# cd /tmp because ioscli generates ioscli.log in current work directory
while(<CMD>) {
chomp;
my ( $vhost, $lpar_name, $lpar_number, $vio_hw_path, $san_status, $vio_adapter, $lpar_adapter, $lpar_hw_path)=split(/,/);
$lpar_name =~ s/^\s+|\s+$//g; # trim spaces; f*ck IBM (empty entries are no empty strings but " " grrrr), IV86769 adds -fmt2, no trimming anymore
$lpar_adapter =~ s/^\s+|\s+$//g; # trim more spaces; same reason
$lpar_hw_path =~ s/^\s+|\s+$//g; # still triming
$vio_adapter =~ s/^\s+|\s+$//g; # still triming
$vfc{$vhost}->{'lpar_name'}=$lpar_name;
$vfc{$vhost}->{'lpar_number'}=$lpar_number;
$vfc{$vhost}->{'vio_adapter'}=$vio_adapter;
$vfc{$vhost}->{'lpar_adapter'}=$lpar_adapter;
$vfc{$vhost}->{'san_status'}=$san_status;
$vfc{$vhost}->{'lpar_hw_path'}=$lpar_hw_path;
if ( $lpar_hw_path =~ /[^-]+\-V[0-9]+\-(C[0-9]+)/ ) {
$vfc{$vhost}->{"lpar_slot"}=$1;
}
}
close(CMD);
my %wwpns;
# get LPAR WWPNs from fcstat
# cd /tmp because ioscli generates ioscli.log in current work directory
open(CMD,"cd /tmp; /usr/ios/cli/ioscli fcstat -client|") or die "Error running ioscli command\n";
# hostname dev wwpn inreqs outreqs ctrlreqs inbytes outbytes DMA_errs Elem_errs Comm_errs
#
# vios fcs0 0x100000109B1DC25C 312994017 31408 8125962 10754518493985 2740539392 0 0 0
# axlpar1 fcs0 0xC0507604F7C30198 123132810 21987139 711500 3837994849924 331345608704 0 0 0
# axlpar2 fcs0 0xC0507604F7C3013E 8282431 5157236 1863108 671551786965 328632340480 0 0 0
while(<CMD>) {
chomp;
my ( $start, $lpar_name, $dev, $wwpn, $rest)=split(/\s+/);
# ignore empty and non-relevant lines
next if ( ! defined $start or $dev eq "dev" );
$wwpn =~ s/0x//; # remove 0x from WWPN
$wwpns{$lpar_name}->{$dev}=$wwpn;
}
close(CMD);
#print Data::Dumper->Dump([\%wwpns]);
# get vhost to lpar mapping from kernel kdb... this takes some time! :-)
# kdb queries are slow. let's query some info with the same kdb call (in "parallel"), 15 seems OK, 20 is NOT
# looking for 2 lines:
# Target vSCSI Adapter Structure vhost0
# client_data.srp_version: 16.aviosserver1 client_data.partition_name: server1
# client_data.partition_number: A
my $parallel_kdb_queries=15; # 15 queries in 1 kdb call. Reduce if you see errors or missing lpar names
my $kdb_count=0;
my $number_of_vhosts= scalar(keys %vscsi);
my $initial_kdb_command= "svCmdIni; svPrQs; ";
my $kdb_command=$initial_kdb_command;
foreach my $vhost ( keys %vscsi ) {
$kdb_count++; $number_of_vhosts--;
$kdb_command .= "svva $vhost; ";
if ( $kdb_count == $parallel_kdb_queries or $number_of_vhosts == 0 ) {
my $vhost_name=""; my $lpar_name=""; my $lpar_number="";
open(CMD,"echo \"$kdb_command\" \|/usr/sbin/kdb -script|" ) or die "Error running /usr/sbin/kdb -script !\n";
while(<CMD>) {
if ( m/\s+Target vSCSI Adapter Structure\s+(\S+)\b/ ) {
$vhost_name=$1;
}
if ( m/client_data.srp_version:\s+\S+\s+client_data.partition_name:\s+(\S+)\b/ ) {
$lpar_name=$1; # lpar_name is only set correctly when LPAR is running
# when lpar is down, the old name before shutdown is used
# when lpar was never active, name is empty. Same applies to defined adapters that made available
}
if ( m/client_data.srp_version:\s+client_data.partition_name:\s+$/ ) {
$lpar_name='none'; # special case of empty vhost
}
if ( m/client_data.partition_number:\s+([\da-fA-F]+)\b/ ) {
$lpar_number=$1; # 0 means "any partition" -> vhost is empty
# this reflects HMC profile values (partition id).
}
if ( $lpar_name ne "" and $vhost_name ne "" and $lpar_number ne "" ) {
$vscsi{$vhost_name}->{"lpar_name"}=$lpar_name;
$vscsi{$vhost_name}->{"lpar_number"}=hex($lpar_number);
$vhost_name=""; $lpar_name=""; $lpar_number=""; #prevent leftover from earlier values
}
}
close(CMD);
$kdb_count=0; $kdb_command=$initial_kdb_command;
}
}
# subroutine to sort the disks numerically rather than alphabetically
sub device_numerically {
$a=~ /\D+(\d+)/;
my $aa=$1;
$b=~ /\D+(\d+)/;
my $bb=$1;
$aa <=> $bb;
}
#print Data::Dumper->Dump([\%vscsi]);
#print Data::Dumper->Dump([\%vfc]);
# read the disk types and availability
open(CMD,"/usr/sbin/lsdev -c disk -F'name subclass type status'|") or die "Error running lsdev -c disk -F'name subclass type'!";
while(<CMD>) {
chomp;
if ( m/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) {
$disks{$1}->{'name'}=$1;
$disks{$1}->{'subclass'}=$2;
$disks{$1}->{'type'}=$3;
$disks{$1}->{'status'}=$4;
}
}
close(CMD);
# get VTD to disk name mapping from ODM
my @vtd_list;
{
local $/="\n\n"; # record delimiter is 2 new lines = paragraph
@vtd_list=qx { /usr/bin/odmget -q "attribute=aix_tdev" CuAt };
}
#CuAt:
# name = "server1_dc1_lun1"
# attribute = "aix_tdev"
# value = "hdisk459"
# type = "R"
# generic = "D"
# rep = "s"
# nls_index = 5
foreach my $vtd ( @vtd_list ) {
my $vtd_name="";
my $vtd_disk="";
if ( $vtd =~ m/\s+name\s+=\s+"(\S+)"/m ) { $vtd_name = $1; }
if ( $vtd =~ m/\s+value\s+=\s+"(\S+)"/m ) { $vtd_disk = $1; }
if ( $vtd_disk ne "" and $vtd_name ne "" ) {
$disks{$vtd_disk}->{'vtd'}=$vtd_name;
}
}
undef @vtd_list;
#print Data::Dumper->Dump([\%disks]);
# get VTD to vhost mapping from ODM
my @vtd_vhost_list;
{
local $/="\n\n";
@vtd_vhost_list=qx { /usr/bin/odmget -q PdDvLn="virtual_target/vtdev/scdisk" CuDv };
}
#CuDv:
# name = "server1_dc1_lun1"
# status = 1
# chgstatus = 0
# ddins = ""
# location = ""
# parent = "vhost50"
# connwhere = "5"
# PdDvLn = "virtual_target/vtdev/scdisk"
foreach my $vtd ( @vtd_vhost_list ) {
my $vtd_name="";
my $vtd_parent="";
if ( $vtd =~ m/\s+name\s+=\s+"(\S+)"/m ) { $vtd_name = $1; }
if ( $vtd =~ m/\s+parent\s+=\s+"(\S+)"/m ) { $vtd_parent = $1; }
if ( $vtd_parent ne "" and $vtd_name ne "" ) {
foreach my $disk ( keys %disks ) {
if ( defined $disks{$disk}->{'vtd'} and $disks{$disk}->{'vtd'} eq $vtd_name ) {
$disks{$disk}->{'vhost'}=$vtd_parent;
$vscsi{$vtd_parent}->{'disks'}->{$disk}=$vtd_name;
last;
}
}
}
}
undef @vtd_vhost_list;
# get all unique_id from ODM if there is a matching disk
my @uuid_list;
{
local $/="\n\n";
@uuid_list=qx { /usr/bin/odmget -q "attribute=unique_id" CuAt };
}
foreach my $uuid ( @uuid_list ) {
my $unique_id="";
my $name="";
if ( $uuid =~ m/\s+name\s+=\s+"(\S+)"/m ) { $name=$1; }
if ( $uuid =~ m/\s+value\s+=\s+"(\S+)"/m ) { $unique_id=$1; }
if ( $name ne "" and $unique_id ne "" and defined $disks{$name} ) {
$disks{$name}->{'unique_id'}=$unique_id;
}
}
undef @uuid_list;
#print Data::Dumper->Dump([\%disks]);
#print Data::Dumper->Dump([\%vscsi]);
# identify various disks from unique_id
sub identify_2145 {
my $disksref = shift;
my $d = shift;
# SVC disks:
# 3321360050768019081CFD800000000000A8404214503IBMfcp
# SVC LUNs visible directly via FC or NPIV but without SDDPCM:
# 33213600507680180867968000000000006E804214503IBMfcp
# SVC LUNs visible through VIO:
# 48333321360050768019081CFD8000000000009D804214503IBMfcp05VDASD03AIXvscsi
# IBM storage WWN always start with 6005076
if ( defined $disksref->{$d}->{'unique_id'} and $disksref->{$d}->{'unique_id'} =~ m/^\S+(6005076......(.....)..........(..)(..))..2145..IBMfcp/ ) {
$disksref->{$d}->{'uuid'}=$1;
$disksref->{$d}->{'box'}=$2;
$disksref->{$d}->{'suid'}="$3:$4";
$disksref->{$d}->{'box_type'}="2145";
# This should work from AIX 5.3 upwards for 2145
# This might work for DS5000 (type 1818) too, although I don't have a machine to test with
return 1; # true
}
return 0; # false
}
sub identify_2107 {
my $disksref = shift;
my $d = shift;
# DS8000 family LUN
# 200B75G6831015607210790003IBMfcp
if ( defined $disksref->{$d}->{'unique_id'} and $disksref->{$d}->{'unique_id'}=~m/^....(.......)((..)(..))..2107900..IBMfcp/ ) {
$disksref->{$d}->{'box'}=$1;
$disksref->{$d}->{'uuid'}="$1_$2"; # this is normally $2 ONLY, but we don't print the box
$disksref->{$d}->{'suid'}="$3:$4";
$disksref->{$d}->{'box_type'}="2107";
# There is a lun_id for DS8000 too, but this contains the same info as $3$4 (0x40XX40XX00000000)
return 1; # true
}
return 0; # false
}
# identify Hitachi VSP GX00 disks
sub identify_htc_vsp_gx00 {
my $disksref = shift;
my $d = shift;
# may also work with Hitachi boxes that use those disk types:
# htc9900mpio
# htchusvmmpio
# htcuspmpio
# htcuspvmpio
# htcvspg1000mpio
# htcvspmpio
# tested with Hitachi VSP G200 without HDLM, just AIX MPIO
# 240C504138FE013006OPEN-V07HITACHIfcp
if ( defined $disksref->{$d}->{'unique_id'} and $disksref->{$d}->{'unique_id'}=~ m/........(....)((..)(..))..OPEN-...HITACHIfcp/ ) {
$disksref->{$d}->{'box'}=$1;
$disksref->{$d}->{'uuid'}="$1_$2";
$disksref->{$d}->{'suid'}="$3:$4";
$disksref->{$d}->{'box_type'}="HTC";
return 1; # true
}
return 0; # false
}
# identify Purestorage disks
sub identify_purestorage {
my $disksref = shift;
my $d = shift;
# puredisk
# tested with Purestorage, box type unknown
# 3A213624A93708D4089CD6DB5405A000113E70AFlashArray04PUREfcp
# Important: The box or array id does not change when a lun is moved later on to another box! Take care!
# see https://www.codyhosterman.com/2016/01/vmfs-snapshots-and-the-flasharray-part-iv-how-to-correlate-a-vmfs-to-a-flasharray-volume/
# and https://www.codyhosterman.com/2018/02/volume-matching-via-the-api-in-purity-5-0/
if ( defined $disksref->{$d}->{'unique_id'} and $disksref->{$d}->{'unique_id'}=~ m/.....624A9370((................)(........))..FlashArray04PUREfcp/ ) {
$disksref->{$d}->{'box'}=$2; # ????
$disksref->{$d}->{'uuid'}="$1";
my $tmpsuid=$3;
$tmpsuid=~ s/^0+//g; # remove leading zeros
$disksref->{$d}->{'suid'}="$tmpsuid";
$disksref->{$d}->{'box_type'}="PURE";
if ( defined $disksref->{$d}->{'lun_id'} ) {
if ( $disksref->{$d}->{'lun_id'} =~ m/0x(?:([\da-f]+)0{12}|0)\b/ ) { # either 0 or value without 12 zeros
$disksref->{$d}->{'scsi_id'}= defined $1 ? hex($1):0;
}
}
return 1; # true
}
return 0; # false
}
# copy & paste from luninfo, few lines removed (like lun_id and scsi_id stuff)
foreach my $disk ( keys %disks ) {
# use type and subclass to separate disks
for ($disks{$disk}{'type'}) {
#2105 - 2105 models (ESS)
#2145 - 2145 models (SVC)
#2147 - 2147 models (SVC)
#1750 - 1750 devices (DS 6000)
#2107 - 2107 devices (DS 8000)
#1724 - 1724 devices (DS 4100)
#1814 - 1814 devices (DS 4200 and DS 4700)
#1722 - 1722 devices (DS 4300)
#1742 - 1742 devices (DS 4400 and DS 4500)
#1815 - 1815 devices (DS 4800)
#1818 - 1818 devices (DS 5000)
if (/2145/) {
identify_2145(\%disks, $disk);
}
elsif (/2107/) {
identify_2107(\%disks, $disk);
}
elsif (/puredisk/) {
identify_purestorage(\%disks, $disk);
}
elsif (/vdisk/) {
identify_2145(\%disks, $disk) or
identify_purestorage(\%disks, $disk) or
identify_2107(\%disks, $disk);
}
elsif (/mpioosdisk/) { # generic AIX MPIO
identify_2145(\%disks, $disk) or
identify_purestorage(\%disks, $disk) or
identify_2107(\%disks, $disk);
}
elsif (/htcvspgx00mpio/) { # Hitachi VSP GX00 MPIO
identify_htc_vsp_gx00(\%disks, $disk);
}
elsif (/scsd/) { # parallel or serial SCSI disk (subclass would be scsi and sas)
delete $disks{$disk}; # we are not interested in those -> remove from hash
}
elsif (/sisarray/) { # SAS RAID arrays
delete $disks{$disk}; # we are (currently) not interested in those -> remove from hash
} else {
print STDERR "Unknown disk type $disks{$disk}{'type'} for disk $disk found!\n";
undef $disks{$disk};
}
}
}
#print Data::Dumper->Dump([\%disks]);
# Output
# -d
if ( $options{'disks'} ) {
my $max_box_len=max(3, map length($_->{'box'}//""), values %disks);
my $max_uid_len=max(5, map length($_->{'suid'}//""), values %disks);
if ( ! $options{'noheader'} ) { # option -H
printf("%-9s %-9s %-4s %6s %-10s %-8s %-16s %-5s %-*s %-*s\n",
"#ADAPTER","STATE","SLOT","LPARID","LPAR","HDISK","VTD","LOC","BOX","UID");
}
# only vscsi vhosts have luns associated
foreach my $vhost (sort device_numerically keys %vscsi) {
# do we have disks connected to this vhost?
if ( scalar ( keys %{$vscsi{$vhost}->{'disks'}}) != 0 ) {
foreach my $disk ( keys %{$vscsi{$vhost}->{'disks'}} ) {
printf("%-9s %-9s %-4s %6s %-10s %-8s %-16s %-5s %-*s %-*s\n",
$vscsi{$vhost}->{'name'},
$vscsi{$vhost}->{'status'},
$vscsi{$vhost}->{'slot'},
$vscsi{$vhost}->{'lpar_number'} // "-",
$vscsi{$vhost}->{'lpar_name'} // "-",
$disk,
$vscsi{$vhost}->{'disks'}->{$disk},
$locations{$disks{$disk}->{'box'}} // "UNKN",
$max_box_len, $disks{$disk}->{'box'} // "UNKN",
$max_uid_len, $disks{$disk}->{'suid'} // "-"
);
}
} else { # empty vhost
printf("%-9s %-9s %-4s %6s %-10s %-8s %-16s %-5s %-7s %-5s\n",
$vscsi{$vhost}->{'name'},
$vscsi{$vhost}->{'status'},
$vscsi{$vhost}->{'slot'},
$vscsi{$vhost}->{'lpar_number'} // "-",
$vscsi{$vhost}->{'lpar_name'} // "-",
'none',
'none',
'-',
'-',
'-'
);
}
}
} else {
# no option
if ( ! $options{'noheader'} ) { # option -H
printf("%-9s %-9s %-4s %-5s %-16s %6s %-10s %-5s %-5s %-16s %14s\n",
"#ADAPTER","STATE","SLOT","HBA","WWPN","LPARID","LPAR","LHBA","VSLOT","LPARWWPN","SAN");
}
# vscsi goes first, some fields are not available -> "-"
foreach my $vhost (sort device_numerically keys %vscsi) {
printf("%-9s %-9s %-4s %-5s %-16s %6s %-10s %-5s %-5s %14s\n",
$vscsi{$vhost}->{'name'},
$vscsi{$vhost}->{'status'},
$vscsi{$vhost}->{'slot'},
"-",
"-",
$vscsi{$vhost}->{'lpar_number'} // "-",
$vscsi{$vhost}->{'lpar_name'} // "-",
"-",
"-",
"-",
"-"
);
}
foreach my $vhost (sort device_numerically keys %vfc) {
printf("%-9s %-9s %-4s %-5s %-16s %6s %-10s %-5s %-5s %-16s %14s\n",
$vfc{$vhost}->{'name'},
$vfc{$vhost}->{'status'},
$vfc{$vhost}->{'slot'},
$vfc{$vhost}->{'vio_adapter'} || "-",
$adapters{$vfc{$vhost}->{'vio_adapter'}}->{'wwpn'} // "-",
$vfc{$vhost}->{'lpar_number'},
$vfc{$vhost}->{'lpar_name'} || "-",
$vfc{$vhost}->{'lpar_adapter'} || "-",
$vfc{$vhost}->{'lpar_slot'} // "-",
$wwpns{$vfc{$vhost}->{'lpar_name'}}->{$vfc{$vhost}->{'lpar_adapter'}} // "-",
$vfc{$vhost}->{'san_status'}
);
}
}
# The end.