| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package IBM::StorageSystem; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 22470 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 38 |  | 
| 4 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 1 |  |  | 1 |  | 559 | use IBM::StorageSystem::Array; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 22 |  | 
| 7 | 1 |  |  | 1 |  | 450 | use IBM::StorageSystem::Disk; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 22 |  | 
| 8 | 1 |  |  | 1 |  | 459 | use IBM::StorageSystem::Drive; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 38 |  | 
| 9 | 1 |  |  | 1 |  | 526 | use IBM::StorageSystem::Enclosure; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 41 |  | 
| 10 | 1 |  |  | 1 |  | 833 | use IBM::StorageSystem::Export; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 31 |  | 
| 11 | 1 |  |  | 1 |  | 828 | use IBM::StorageSystem::Fabric; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 12 | 1 |  |  | 1 |  | 821 | use IBM::StorageSystem::FileSystem; | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 42 |  | 
| 13 | 1 |  |  | 1 |  | 908 | use IBM::StorageSystem::Health; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 14 | 1 |  |  | 1 |  | 679 | use IBM::StorageSystem::Host; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 15 | 1 |  |  | 1 |  | 629 | use IBM::StorageSystem::Interface; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 33 |  | 
| 16 | 1 |  |  | 1 |  | 531 | use IBM::StorageSystem::IOGroup; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 24 |  | 
| 17 | 1 |  |  | 1 |  | 492 | use IBM::StorageSystem::Mount; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 25 |  | 
| 18 | 1 |  |  | 1 |  | 555 | use IBM::StorageSystem::Node; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 25 |  | 
| 19 | 1 |  |  | 1 |  | 472 | use IBM::StorageSystem::Pool; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 25 |  | 
| 20 | 1 |  |  | 1 |  | 573 | use IBM::StorageSystem::Replication; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 29 |  | 
| 21 | 1 |  |  | 1 |  | 595 | use IBM::StorageSystem::Service; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 22 | 1 |  |  | 1 |  | 484 | use IBM::StorageSystem::Task; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 24 |  | 
| 23 | 1 |  |  | 1 |  | 488 | use IBM::StorageSystem::Quota; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 24 | 1 |  |  | 1 |  | 596 | use IBM::StorageSystem::VDisk; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 34 |  | 
| 25 | 1 |  |  | 1 |  | 564 | use IBM::StorageSystem::Statistic; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 26 | 1 |  |  | 1 |  | 631 | use IBM::StorageSystem::StatisticsSet; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 29 |  | 
| 27 | 1 |  |  | 1 |  | 709 | use IBM::StorageSystem::Statistic::ClusterThroughput; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 31 |  | 
| 28 | 1 |  |  | 1 |  | 570 | use IBM::StorageSystem::Statistic::ClusterClientThroughput; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 33 |  | 
| 29 | 1 |  |  | 1 |  | 630 | use IBM::StorageSystem::Statistic::ClusterCreateDeleteLatency; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 32 |  | 
| 30 | 1 |  |  | 1 |  | 642 | use IBM::StorageSystem::Statistic::ClusterCreateDeleteOperations; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 31 | 1 |  |  | 1 |  | 535 | use IBM::StorageSystem::Statistic::ClusterOpenCloseLatency; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 32 | 1 |  |  | 1 |  | 562 | use IBM::StorageSystem::Statistic::ClusterOpenCloseOperations; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 31 |  | 
| 33 | 1 |  |  | 1 |  | 537 | use IBM::StorageSystem::Statistic::ClusterReadWriteLatency; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 26 |  | 
| 34 | 1 |  |  | 1 |  | 562 | use IBM::StorageSystem::Statistic::ClusterReadWriteOperations; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 35 | 1 |  |  | 1 |  | 6 | use IBM::StorageSystem::Statistic::Node::Memory; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 36 | 1 |  |  | 1 |  | 7 | use IBM::StorageSystem::Statistic::Node::CPU; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 21 |  | 
| 37 | 1 |  |  | 1 |  | 579 | use IBM::StorageSystem::Statistic::Pool::Throughput; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 38 | 1 |  |  | 1 |  | 1520 | use Net::OpenSSH; | 
|  | 1 |  |  |  |  | 51204 |  | 
|  | 1 |  |  |  |  | 95 |  | 
| 39 | 1 |  |  | 1 |  | 14 | use Carp qw(croak); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 551 |  | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | our $VERSION = '0.045'; | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | our @ATTR = qw(auth_service_cert_set auth_service_configured auth_service_enabled | 
| 44 |  |  |  |  |  |  | auth_service_pwd_set auth_service_type auth_service_url auth_service_user_name | 
| 45 |  |  |  |  |  |  | bandwidth cluster_isns_IP_address cluster_locale cluster_ntp_IP_address code_level | 
| 46 |  |  |  |  |  |  | console_IP email_contact email_contact2 email_contact2_alternate email_contact2_primary | 
| 47 |  |  |  |  |  |  | email_contact_alternate email_contact_location email_contact_primary email_reply | 
| 48 |  |  |  |  |  |  | email_state gm_inter_cluster_delay_simulation gm_intra_cluster_delay_simulation | 
| 49 |  |  |  |  |  |  | gm_link_tolerance gm_max_host_delay has_nas_key id id_alias inventory_mail_interval | 
| 50 |  |  |  |  |  |  | iscsi_auth_method iscsi_chap_secret layer location name partnership rc_buffer_size | 
| 51 |  |  |  |  |  |  | relationship_bandwidth_limit space_allocated_to_vdisks space_in_mdisk_grps | 
| 52 |  |  |  |  |  |  | statistics_frequency statistics_status stats_threshold tier tier_capacity tier_free_capacity | 
| 53 |  |  |  |  |  |  | time_zone total_allocated_extent_capacity total_free_space total_mdisk_capacity | 
| 54 |  |  |  |  |  |  | total_overallocation total_used_capacity total_vdisk_capacity total_vdiskcopy_capacity); | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | our @STAT = qw(compression_cpu_pc cpu_pc drive_r_io drive_r_mb drive_r_ms drive_w_io | 
| 57 |  |  |  |  |  |  | drive_w_mb drive_w_ms fc_io fc_mb iscsi_io iscsi_mb mdisk_r_io mdisk_r_mb mdisk_r_ms | 
| 58 |  |  |  |  |  |  | mdisk_w_io mdisk_w_mb mdisk_w_ms sas_io sas_mb total_cache_pc vdisk_r_io vdisk_r_mb | 
| 59 |  |  |  |  |  |  | vdisk_r_ms vdisk_w_io vdisk_w_mb vdisk_w_ms write_cache_pc); | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | $|++; | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | foreach my $attr ( @ATTR ) { | 
| 64 |  |  |  |  |  |  | { | 
| 65 | 1 |  |  | 1 |  | 7 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 129 |  | 
| 66 |  |  |  |  |  |  | *{ __PACKAGE__ .'::'. $attr } = sub { | 
| 67 | 0 |  |  | 0 |  |  | my( $self, $val ) = @_; | 
| 68 | 0 | 0 |  |  |  |  | $val =~ s/\#/no/ if $val; | 
| 69 | 0 | 0 |  |  |  |  | $self->{$attr} = $val if $val; | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 0 |  |  |  |  |  | return $self->{$attr} | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | foreach my $stat ( @STAT ) { | 
| 77 |  |  |  |  |  |  | { | 
| 78 | 1 |  |  | 1 |  | 6 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 210 |  | 
| 79 |  |  |  |  |  |  | *{ __PACKAGE__ .'::'. $stat } = sub { | 
| 80 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 81 | 0 | 0 |  |  |  |  | $self->stats_threshold or return $self->{$stat}; | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 0 | 0 |  |  |  |  | return ( ( time - $self->{$stat}->{ts} ) > $self->stats_threshold | 
| 84 |  |  |  |  |  |  | ? $self->{$stat}->refresh | 
| 85 |  |  |  |  |  |  | : $self->{$stat} ) | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | our $STATS = { | 
| 91 |  |  |  |  |  |  | cluster_throughput => { | 
| 92 |  |  |  |  |  |  | cmd     => '-g cluster_throughput', | 
| 93 |  |  |  |  |  |  | class   => 'IBM::StorageSystem::Statistic::ClusterThroughput' | 
| 94 |  |  |  |  |  |  | }, | 
| 95 |  |  |  |  |  |  | cluster_client_throughput => { | 
| 96 |  |  |  |  |  |  | cmd     => '-g client_throughput', | 
| 97 |  |  |  |  |  |  | class   => 'IBM::StorageSystem::Statistic::ClusterClientThroughput' | 
| 98 |  |  |  |  |  |  | }, | 
| 99 |  |  |  |  |  |  | cluster_create_delete_latency => { | 
| 100 |  |  |  |  |  |  | cmd	=> '-g cluster_create_delete_latency', | 
| 101 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Statistic::ClusterCreateDeleteLatency' | 
| 102 |  |  |  |  |  |  | }, | 
| 103 |  |  |  |  |  |  | cluster_create_delete_operations => { | 
| 104 |  |  |  |  |  |  | cmd	=> '-g cluster_create_delete_operations', | 
| 105 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Statistic::ClusterCreateDeleteOperations' | 
| 106 |  |  |  |  |  |  | }, | 
| 107 |  |  |  |  |  |  | cluster_open_close_latency => { | 
| 108 |  |  |  |  |  |  | cmd	=> '-g cluster_open_close_latency', | 
| 109 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Statistic::ClusterOpenCloseLatency' | 
| 110 |  |  |  |  |  |  | }, | 
| 111 |  |  |  |  |  |  | cluster_open_close_operations => { | 
| 112 |  |  |  |  |  |  | cmd	=> '-g cluster_open_close_operations', | 
| 113 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Statistic::ClusterOpenCloseOperations' | 
| 114 |  |  |  |  |  |  | }, | 
| 115 |  |  |  |  |  |  | cluster_read_write_operations => { | 
| 116 |  |  |  |  |  |  | cmd	=> '-g cluster_read_write_operations', | 
| 117 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Statistic::ClusterReadWriteOperations' | 
| 118 |  |  |  |  |  |  | }, | 
| 119 |  |  |  |  |  |  | cluster_read_write_latency => { | 
| 120 |  |  |  |  |  |  | cmd	=> '-g cluster_read_write_latency', | 
| 121 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Statistic::ClusterReadWriteLatency' | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  | }; | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | foreach my $stat ( keys %{ $STATS } ) { | 
| 126 |  |  |  |  |  |  | { | 
| 127 | 1 |  |  | 1 |  | 6 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 502 |  | 
| 128 |  |  |  |  |  |  | *{ __PACKAGE__ .'::'. $stat } = | 
| 129 |  |  |  |  |  |  | sub { | 
| 130 | 0 |  |  | 0 |  |  | my( $self, $t ) = @_; | 
| 131 | 0 |  | 0 |  |  |  | $t ||= 'minute'; | 
| 132 | 0 |  |  |  |  |  | my $stats = $self->__lsperfdata( cmd   => "$STATS->{$stat}->{cmd} -t $t", | 
| 133 |  |  |  |  |  |  | class => $STATS->{$stat}->{class} | 
| 134 |  |  |  |  |  |  | ); | 
| 135 | 0 |  |  |  |  |  | return $stats | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | # Our object hash for programmatic generation of methods. | 
| 142 |  |  |  |  |  |  | # | 
| 143 |  |  |  |  |  |  | # Each hash represents an object type that we will generate methods for - for example, given the below; | 
| 144 |  |  |  |  |  |  | # | 
| 145 |  |  |  |  |  |  | # 	drive => | 
| 146 |  |  |  |  |  |  | #		bcmd	=> 'lsdrive -nohdr -delim :', | 
| 147 |  |  |  |  |  |  | #		cmd	=> 'lsdrive', | 
| 148 |  |  |  |  |  |  | #		id	=> 'id', | 
| 149 |  |  |  |  |  |  | #		class	=> 'IBM::StorageSystem::Drive', | 
| 150 |  |  |  |  |  |  | #		type	=> 'drive' | 
| 151 |  |  |  |  |  |  | #	}, | 
| 152 |  |  |  |  |  |  | # | 
| 153 |  |  |  |  |  |  | # drive	- 	is the object type - in this case a drive in Storwize nomenclature representing a physical | 
| 154 |  |  |  |  |  |  | # 		hard drive. | 
| 155 |  |  |  |  |  |  | # | 
| 156 |  |  |  |  |  |  | # bcmd	-	is the base cmd to be executed in the Storwize CLI to retrieve a list of drive objects.  This | 
| 157 |  |  |  |  |  |  | #		parameter is only required for object types where object enumeration is required as a prequisite | 
| 158 |  |  |  |  |  |  | #		to execution of object specific commands. | 
| 159 |  |  |  |  |  |  | # | 
| 160 |  |  |  |  |  |  | #		Using the example above for 'lsdrive -nohdr -delim :', the base cmd is necessary to first enumerate | 
| 161 |  |  |  |  |  |  | #		all drives to obtain a summary listing of all drives and their ID's.  The ID's can then be used in | 
| 162 |  |  |  |  |  |  | #		the 'cmd' command (e.g. lsdrive 1) to obtain detailed information on each drive. | 
| 163 |  |  |  |  |  |  | # | 
| 164 |  |  |  |  |  |  | #		As there is no way in which to obtain detailed information of some objects without specifying the | 
| 165 |  |  |  |  |  |  | #		object ID, the bcmd is necessary to first produce a list of these ID's.  Contrast the operation of | 
| 166 |  |  |  |  |  |  | #		the lsdrive command and the need for an ID parameter to obtain detailed information with the operation | 
| 167 |  |  |  |  |  |  | #		of the lsnode command, which provides detailed information on all nodes without any parameters. | 
| 168 |  |  |  |  |  |  | # | 
| 169 |  |  |  |  |  |  | #		*NOTE* that the bcmd is	not necessary for all objects - only those which there is no single command | 
| 170 |  |  |  |  |  |  | #		to produce a detailed listing without specifying an ID type parameter.  Note also, that you probably | 
| 171 |  |  |  |  |  |  | #		want to specify additional options to the bcmd command (like -nohdr and -delim :) to prevent header | 
| 172 |  |  |  |  |  |  | #		information from being parsed. | 
| 173 |  |  |  |  |  |  | # | 
| 174 |  |  |  |  |  |  | # cmd	-	is the CLI command that will be used to retrieve information about the object.  *NOTE* the above information | 
| 175 |  |  |  |  |  |  | #		on the use of the bcmd value and how this command works in conjunction with it. | 
| 176 |  |  |  |  |  |  | # | 
| 177 |  |  |  |  |  |  | # id	-	is a single field in the CLI command output (or multiple fields concatenated by colon) that is | 
| 178 |  |  |  |  |  |  | #		able to uniquely identify each object in a global context for this object type.  For example; | 
| 179 |  |  |  |  |  |  | #		in a systems with more than one enclosure it is necessary to identify an enclosure PSU by specifying | 
| 180 |  |  |  |  |  |  | #		both the enclosure id and the PSU id - e.g. Enclosure 2, PSU 1 - which when separated by a colon | 
| 181 |  |  |  |  |  |  | #		would become 2:1 which is sufficient to uniquely identify a enclsoure PSU in a system of any number | 
| 182 |  |  |  |  |  |  | #		of enclosures. | 
| 183 |  |  |  |  |  |  | # | 
| 184 |  |  |  |  |  |  | # class	-	the Perl package namespace into which this object will be blessed as a class. | 
| 185 |  |  |  |  |  |  | # | 
| 186 |  |  |  |  |  |  | # type	-	the 'type' of object - this is usually the same as the root key type, however it must also be | 
| 187 |  |  |  |  |  |  | #		globally unique within this class.  This value is used internally to implement object caching | 
| 188 |  |  |  |  |  |  | #		and is not used in method naming so need not necessarily intuitive to a user. | 
| 189 |  |  |  |  |  |  | # | 
| 190 |  |  |  |  |  |  | # sl	-	this optional parameter is used to designate the CLI command output type as 'single-line' - this | 
| 191 |  |  |  |  |  |  | #		is in contrast to 'multi-line' output.  In general, CLI command output falls into two categories; | 
| 192 |  |  |  |  |  |  | #		'single-line' output contains output on multiple objects with each line representative of a unique | 
| 193 |  |  |  |  |  |  | #		object - this may also be referred to as 'row-based' data.  An example of such output is the 'lsnode' | 
| 194 |  |  |  |  |  |  | #		command. | 
| 195 |  |  |  |  |  |  | # | 
| 196 |  |  |  |  |  |  | #		'multi-line' output contains output about a single object as key-value pairs over multiple lines, | 
| 197 |  |  |  |  |  |  | #		may also be referred to as 'columnar' data.  An output of such output is the 'lsdrive' command when | 
| 198 |  |  |  |  |  |  | #		executed with a valid drive id parameter (e.g. lsdrive 1). | 
| 199 |  |  |  |  |  |  | # | 
| 200 |  |  |  |  |  |  | #		By default, CLI commands are assumed to use columnar output, so it is necessary for any commands | 
| 201 |  |  |  |  |  |  | #		using row-based output to also specify a true value for the 'sl' key to ensure that the output is | 
| 202 |  |  |  |  |  |  | #		parsed correctly.  CLI commands that output columnar data should not specify this command. | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | our $OBJ = {	drive => { | 
| 205 |  |  |  |  |  |  | bcmd	=> 'lsdrive -nohdr -delim :', | 
| 206 |  |  |  |  |  |  | cmd	=> 'lsdrive -bytes', | 
| 207 |  |  |  |  |  |  | id	=> 'id', | 
| 208 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Drive', | 
| 209 |  |  |  |  |  |  | type	=> 'drive' | 
| 210 |  |  |  |  |  |  | }, | 
| 211 |  |  |  |  |  |  | vdisk => { | 
| 212 |  |  |  |  |  |  | bcmd	=> 'lsvdisk -nohdr -delim :', | 
| 213 |  |  |  |  |  |  | cmd	=> 'lsvdisk -bytes', | 
| 214 |  |  |  |  |  |  | id	=> 'id', | 
| 215 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::VDisk', | 
| 216 |  |  |  |  |  |  | type	=> 'vdisk' | 
| 217 |  |  |  |  |  |  | }, | 
| 218 |  |  |  |  |  |  | disk => { | 
| 219 |  |  |  |  |  |  | cmd	=> 'lsdisk -Y -v', | 
| 220 |  |  |  |  |  |  | id	=> 'Name', | 
| 221 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Disk', | 
| 222 |  |  |  |  |  |  | type	=> 'disk', | 
| 223 |  |  |  |  |  |  | sl	=> 1 | 
| 224 |  |  |  |  |  |  | }, | 
| 225 |  |  |  |  |  |  | enclosure => { | 
| 226 |  |  |  |  |  |  | bcmd	=> 'lsenclosure -nohdr -delim :', | 
| 227 |  |  |  |  |  |  | cmd	=> 'lsenclosure', | 
| 228 |  |  |  |  |  |  | id	=> 'id', | 
| 229 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Enclosure', | 
| 230 |  |  |  |  |  |  | type	=> 'enclosure' | 
| 231 |  |  |  |  |  |  | }, | 
| 232 |  |  |  |  |  |  | host => { | 
| 233 |  |  |  |  |  |  | bcmd    => 'lshost -nohdr -delim :', | 
| 234 |  |  |  |  |  |  | cmd     => 'lshost', | 
| 235 |  |  |  |  |  |  | id      => 'id', | 
| 236 |  |  |  |  |  |  | class   => 'IBM::StorageSystem::Host', | 
| 237 |  |  |  |  |  |  | # so we don't clobber IBM::StorageSystem::host variable | 
| 238 |  |  |  |  |  |  | type    => 'IBM::StorageSystem::Host::host' | 
| 239 |  |  |  |  |  |  | }, | 
| 240 |  |  |  |  |  |  | enclosurebattery => { | 
| 241 |  |  |  |  |  |  | cmd     => 'lsenclosurebattery -delim :', | 
| 242 |  |  |  |  |  |  | id      => 'enclosure_id:battery_id', | 
| 243 |  |  |  |  |  |  | class   => 'IBM::StorageSystem::EnclosureBattery', | 
| 244 |  |  |  |  |  |  | type    => 'enclosurebattery', | 
| 245 |  |  |  |  |  |  | sl      => 1 | 
| 246 |  |  |  |  |  |  | }, | 
| 247 |  |  |  |  |  |  | fabric => { | 
| 248 |  |  |  |  |  |  | cmd     => 'lsfabric -delim :', | 
| 249 |  |  |  |  |  |  | id      => 'local_wwpn:remote_wwpn', | 
| 250 |  |  |  |  |  |  | class   => 'IBM::StorageSystem::Fabric', | 
| 251 |  |  |  |  |  |  | type    => 'fabric', | 
| 252 |  |  |  |  |  |  | sl      => 1 | 
| 253 |  |  |  |  |  |  | }, | 
| 254 |  |  |  |  |  |  | array => { | 
| 255 |  |  |  |  |  |  | bcmd	=> 'lsarray -nohdr -delim :', | 
| 256 |  |  |  |  |  |  | cmd	=> 'lsarray -bytes', | 
| 257 |  |  |  |  |  |  | id	=> 'mdisk_id', | 
| 258 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Array', | 
| 259 |  |  |  |  |  |  | type 	=> 'array' | 
| 260 |  |  |  |  |  |  | }, | 
| 261 |  |  |  |  |  |  | export => { | 
| 262 |  |  |  |  |  |  | cmd	=> 'lsexport -Y -v', | 
| 263 |  |  |  |  |  |  | id	=> 'Name:Path', | 
| 264 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Export', | 
| 265 |  |  |  |  |  |  | type	=> 'export', | 
| 266 |  |  |  |  |  |  | sl	=> 1 | 
| 267 |  |  |  |  |  |  | }, | 
| 268 |  |  |  |  |  |  | mount => { | 
| 269 |  |  |  |  |  |  | cmd	=> 'lsmount -Y -v', | 
| 270 |  |  |  |  |  |  | id	=> 'File_system', | 
| 271 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Mount', | 
| 272 |  |  |  |  |  |  | type	=> 'mount', | 
| 273 |  |  |  |  |  |  | sl	=> 1 | 
| 274 |  |  |  |  |  |  | }, | 
| 275 |  |  |  |  |  |  | node => { | 
| 276 |  |  |  |  |  |  | cmd	=> 'lsnode -Y -v', | 
| 277 |  |  |  |  |  |  | id	=> 'Hostname', | 
| 278 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Node', | 
| 279 |  |  |  |  |  |  | type	=> 'node', | 
| 280 |  |  |  |  |  |  | sl	=> 1 | 
| 281 |  |  |  |  |  |  | }, | 
| 282 |  |  |  |  |  |  | health => { | 
| 283 |  |  |  |  |  |  | cmd	=> 'lshealth -Y', | 
| 284 |  |  |  |  |  |  | id	=> 'Host:Sensor', | 
| 285 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Health', | 
| 286 |  |  |  |  |  |  | type	=> 'health', | 
| 287 |  |  |  |  |  |  | sl	=> 1 | 
| 288 |  |  |  |  |  |  | }, | 
| 289 |  |  |  |  |  |  | iogroup => { | 
| 290 |  |  |  |  |  |  | bcmd	=> 'lsiogrp -nohdr -delim :', | 
| 291 |  |  |  |  |  |  | cmd	=> 'lsiogrp -bytes', | 
| 292 |  |  |  |  |  |  | id	=> 'id', | 
| 293 |  |  |  |  |  |  | type	=> 'iogroup', | 
| 294 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::IOGroup' | 
| 295 |  |  |  |  |  |  | }, | 
| 296 |  |  |  |  |  |  | filesystem => { | 
| 297 |  |  |  |  |  |  | cmd	=> 'lsfs -Y -v', | 
| 298 |  |  |  |  |  |  | id	=> 'Device_name', | 
| 299 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::FileSystem', | 
| 300 |  |  |  |  |  |  | type	=> 'fs', | 
| 301 |  |  |  |  |  |  | sl	=> 1 | 
| 302 |  |  |  |  |  |  | }, | 
| 303 |  |  |  |  |  |  | service => { | 
| 304 |  |  |  |  |  |  | cmd	=> 'lsservice -Y', | 
| 305 |  |  |  |  |  |  | id	=> 'Name', | 
| 306 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Service', | 
| 307 |  |  |  |  |  |  | type	=> 'service', | 
| 308 |  |  |  |  |  |  | sl	=> 1 | 
| 309 |  |  |  |  |  |  | }, | 
| 310 |  |  |  |  |  |  | task => { | 
| 311 |  |  |  |  |  |  | cmd	=> 'lstask -Y -v', | 
| 312 |  |  |  |  |  |  | id	=> 'Name', | 
| 313 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Task', | 
| 314 |  |  |  |  |  |  | type	=> 'task', | 
| 315 |  |  |  |  |  |  | sl	=> 1 | 
| 316 |  |  |  |  |  |  | }, | 
| 317 |  |  |  |  |  |  | replication => { | 
| 318 |  |  |  |  |  |  | cmd	=> 'lsrepl -Y', | 
| 319 |  |  |  |  |  |  | id	=> 'log_Id', | 
| 320 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Replication', | 
| 321 |  |  |  |  |  |  | type	=> 'replication', | 
| 322 |  |  |  |  |  |  | sl	=> 1 | 
| 323 |  |  |  |  |  |  | }, | 
| 324 |  |  |  |  |  |  | quota => { | 
| 325 |  |  |  |  |  |  | cmd	=> 'lsquota -Y', | 
| 326 |  |  |  |  |  |  | id	=> 'Cluster:Device:Type:ID', | 
| 327 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Quota', | 
| 328 |  |  |  |  |  |  | type	=> 'quota', | 
| 329 |  |  |  |  |  |  | sl	=> 1 | 
| 330 |  |  |  |  |  |  | }, | 
| 331 |  |  |  |  |  |  | interface => { | 
| 332 |  |  |  |  |  |  | cmd	=> 'lsnwinterface -x -Y', | 
| 333 |  |  |  |  |  |  | id	=> 'Node:Interface', | 
| 334 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Interface', | 
| 335 |  |  |  |  |  |  | type	=> 'interface', | 
| 336 |  |  |  |  |  |  | sl	=> 1 | 
| 337 |  |  |  |  |  |  | }, | 
| 338 |  |  |  |  |  |  | pool => { | 
| 339 |  |  |  |  |  |  | cmd	=> 'lspool -Y', | 
| 340 |  |  |  |  |  |  | id	=> 'Filesystem:Name', | 
| 341 |  |  |  |  |  |  | class	=> 'IBM::StorageSystem::Pool', | 
| 342 |  |  |  |  |  |  | type	=> 'pool', | 
| 343 |  |  |  |  |  |  | sl	=> 1 | 
| 344 |  |  |  |  |  |  | } | 
| 345 |  |  |  |  |  |  | }; | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | foreach my $obj ( keys %{ $OBJ } ) { | 
| 348 |  |  |  |  |  |  | { | 
| 349 | 1 |  |  | 1 |  | 6 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 2436 |  | 
| 350 |  |  |  |  |  |  | my $m = 'get_'.$obj.'s'; | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | *{ __PACKAGE__ ."::$obj" } = sub { | 
| 353 | 0 |  |  | 0 |  |  | my ( $self, $id ) = @_; | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 0 | 0 |  |  |  |  | return ( $self->{$obj}->{$id}	? $self->{$obj}->{$id} | 
| 356 |  |  |  |  |  |  | : $self->$m( $id ) ) | 
| 357 |  |  |  |  |  |  | }; | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | *{ __PACKAGE__ .'::get_'. $obj } = sub { | 
| 360 | 0 |  |  | 0 |  |  | return $_[0]->$m( $_[1] ) | 
| 361 |  |  |  |  |  |  | }; | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | if ( $OBJ->{$obj}->{sl} ) { | 
| 364 |  |  |  |  |  |  | *{ __PACKAGE__ . "::$m" } = sub { | 
| 365 | 0 |  |  | 0 |  |  | my ( $self, $id ) = @_; | 
| 366 | 0 |  |  |  |  |  | my %args = (	cmd	=> $OBJ->{$obj}->{cmd}, | 
| 367 |  |  |  |  |  |  | class	=> $OBJ->{$obj}->{class}, | 
| 368 |  |  |  |  |  |  | type	=> $OBJ->{$obj}->{type}, | 
| 369 |  |  |  |  |  |  | id	=> $OBJ->{$obj}->{id} | 
| 370 |  |  |  |  |  |  | ); | 
| 371 | 0 |  |  |  |  |  | my @res = $self->__get_sl_objects( %args ); | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 0 | 0 |  |  |  |  | return ( defined $id ? $self->{ $OBJ->{$obj}->{type} }->{$id} : @res ) | 
| 374 |  |  |  |  |  |  | } | 
| 375 |  |  |  |  |  |  | } | 
| 376 |  |  |  |  |  |  | else { | 
| 377 |  |  |  |  |  |  | *{ __PACKAGE__ . "::$m" } = sub { | 
| 378 | 0 |  |  | 0 |  |  | my ( $self, $id ) = @_; | 
| 379 | 0 |  |  |  |  |  | my @objs = map { ( split /:/, $_ )[0] } | 
|  | 0 |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | split /\n/, $self->__cmd( $OBJ->{$obj}->{bcmd} ); | 
| 381 |  |  |  |  |  |  |  | 
| 382 | 0 |  |  |  |  |  | my %args = (	cmd	=> $OBJ->{$obj}->{cmd}, | 
| 383 |  |  |  |  |  |  | objects => [@objs], | 
| 384 |  |  |  |  |  |  | id	=> $OBJ->{$obj}->{id}, | 
| 385 |  |  |  |  |  |  | class	=> $OBJ->{$obj}->{class}, | 
| 386 |  |  |  |  |  |  | type	=> $OBJ->{$obj}->{type} | 
| 387 |  |  |  |  |  |  | ); | 
| 388 | 0 |  |  |  |  |  | my @res = $self->__get_ml_objects( %args ); | 
| 389 |  |  |  |  |  |  |  | 
| 390 | 0 | 0 |  |  |  |  | return ( defined $id ? $self->{ $OBJ->{$obj}->{type} }->{$id} : @res ) | 
| 391 |  |  |  |  |  |  | } | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  | } | 
| 394 |  |  |  |  |  |  | } | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | sub new { | 
| 397 | 0 |  |  | 0 | 1 |  | my ($class, %args) = @_; | 
| 398 | 0 |  |  |  |  |  | my $self = bless {} , $class; | 
| 399 | 0 | 0 |  |  |  |  | $args{user}	? $self->{user}	= $args{user}		: croak 'Mandatory parameter "user" not given'; | 
| 400 | 0 | 0 |  |  |  |  | $args{host}	? $self->{host}	= $args{host}		: croak 'Mandatory parameter "host" not given'; | 
| 401 | 0 | 0 |  |  |  |  | $args{key_path}	? $self->{key_path} = $args{key_path}	: croak 'Mandatory parameter "key_path" not given'; | 
| 402 | 0 |  |  |  |  |  | my %opts = ( user => $self->{user}, key_path => $self->{key_path}, batch_mode => 1, master_opts => '-q' ); | 
| 403 | 0 |  |  |  |  |  | $self->{ssh} = Net::OpenSSH->new( $args{host}, %opts ); | 
| 404 | 0 | 0 |  |  |  |  | $self->{ssh}->error and croak 'Could not create Net::OpenSSH object: ' . $self->{ssh}->error . "\n"; | 
| 405 |  |  |  |  |  |  |  | 
| 406 | 0 | 0 |  |  |  |  | unless ( $args{no_stats} ) { | 
| 407 | 0 |  |  |  |  |  | $self->__lssystem; | 
| 408 | 0 |  |  |  |  |  | $self->refresh_system_stats; | 
| 409 | 0 | 0 |  |  |  |  | $self->{stats_threshold} = ( $args{stats_threshold} ? $args{stats_threshold} : 0 ); | 
| 410 |  |  |  |  |  |  | } | 
| 411 |  |  |  |  |  |  |  | 
| 412 | 0 |  |  |  |  |  | return $self | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | sub refresh_system_stats { | 
| 416 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 417 |  |  |  |  |  |  |  | 
| 418 | 0 |  |  |  |  |  | foreach my $stat ( splice @{ [ split /\n/, $self->__cmd( 'lssystemstats -gui -delim :' ) ] }, 1 ) { | 
|  | 0 |  |  |  |  |  |  | 
| 419 | 0 |  |  |  |  |  | my ( $name, $epoch, $current, $peak, $peak_time, $peak_epoch ) = split /:/, $stat; | 
| 420 | 0 |  |  |  |  |  | $self->{$name} = IBM::StorageSystem::Statistic->new(	$self, | 
| 421 |  |  |  |  |  |  | name	=> $name, | 
| 422 |  |  |  |  |  |  | epoch	=> $epoch, | 
| 423 |  |  |  |  |  |  | current => $current, | 
| 424 |  |  |  |  |  |  | peak	=> $peak, | 
| 425 |  |  |  |  |  |  | peak_time => $peak_time, | 
| 426 |  |  |  |  |  |  | peak_epoch => $peak_epoch | 
| 427 |  |  |  |  |  |  | ); | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | sub __lssystem { | 
| 432 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 433 | 0 |  |  |  |  |  | my ( %a, %dkeys ); | 
| 434 | 0 |  |  |  |  |  | my @output = split /\n/, ( split /\n\n/, $self->__cmd( 'lssystem' ) )[0]; | 
| 435 | 0 |  |  |  |  |  | %dkeys = map { $_ => $dkeys{ $_ }++ } | 
|  | 0 |  |  |  |  |  |  | 
| 436 | 0 |  |  |  |  |  | map { ( split /\s/, $_ )[0] } @output; | 
| 437 |  |  |  |  |  |  |  | 
| 438 | 0 |  |  |  |  |  | for ( @output ) { | 
| 439 | 0 | 0 |  |  |  |  | last if /^\s*$/; | 
| 440 | 0 |  |  |  |  |  | s/$/ -/; | 
| 441 | 0 |  |  |  |  |  | my ( $var, $val ) = ( split /\s/ )[0,1]; | 
| 442 |  |  |  |  |  |  |  | 
| 443 | 0 | 0 |  |  |  |  | if ( $dkeys{ $var } >= 1 ) { | 
| 444 | 0 |  |  |  |  |  | push @{ $self->{$var} }, $val | 
|  | 0 |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  | else { | 
| 447 | 0 |  |  |  |  |  | $self->{$var} = $val | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  |  | 
| 451 | 0 |  |  |  |  |  | return $self | 
| 452 |  |  |  |  |  |  | } | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | sub __lsperfdata { | 
| 455 | 0 |  |  | 0 |  |  | my ( $self, %args ) = @_; | 
| 456 | 0 |  |  |  |  |  | my $stats = IBM::StorageSystem::StatisticsSet->new; | 
| 457 | 0 |  |  |  |  |  | my @output = split /\n/, $self->__cmd( "lsperfdata $args{cmd}" ); | 
| 458 | 0 |  |  |  |  |  | shift @output; | 
| 459 | 0 |  |  |  |  |  | pop @output; | 
| 460 |  |  |  |  |  |  |  | 
| 461 | 0 |  |  |  |  |  | foreach my $line ( @output ) { | 
| 462 | 0 |  |  |  |  |  | my @values = split /,/, $line; | 
| 463 | 0 |  |  |  |  |  | $stats->__push( $args{class}->new( @values ) ) | 
| 464 |  |  |  |  |  |  | } | 
| 465 |  |  |  |  |  |  |  | 
| 466 | 0 |  |  |  |  |  | return $stats; | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | sub __cmd { | 
| 470 | 0 |  |  | 0 |  |  | return $_[0]->{ssh}->capture( $_[1] ) | 
| 471 |  |  |  |  |  |  | } | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | # __get_sl_objects - Get Single Line Objects | 
| 474 |  |  |  |  |  |  | # This method is used to parse CLI output where information is returned | 
| 475 |  |  |  |  |  |  | # in a row-based format - e.g. | 
| 476 |  |  |  |  |  |  | # | 
| 477 |  |  |  |  |  |  | #	Column_1, Column_2, Column_3, ... Column_N | 
| 478 |  |  |  |  |  |  | #	Value_1 , Value_2,  Value_3,  ... Value_N | 
| 479 |  |  |  |  |  |  | #	Value_1 , Value_2,  Value_3,  ... Value_N | 
| 480 |  |  |  |  |  |  | #	Value_1 , Value_2,  Value_3,  ... Value_N | 
| 481 |  |  |  |  |  |  | # | 
| 482 |  |  |  |  |  |  | # To parse this data into an array of objects, we treat each row as a single object. | 
| 483 |  |  |  |  |  |  | # We split the column headers on a delimeter (usually :) and use each header as a hash | 
| 484 |  |  |  |  |  |  | # key for the corresponding value in each row - i.e. | 
| 485 |  |  |  |  |  |  | # | 
| 486 |  |  |  |  |  |  | #	Column_1 => Value_1, | 
| 487 |  |  |  |  |  |  | #	Column_2 => Value_2 ...etc. | 
| 488 |  |  |  |  |  |  | # | 
| 489 |  |  |  |  |  |  | # Each hash is then passed to the constructor for this object type (given as the 'class' | 
| 490 |  |  |  |  |  |  | # argument in our %args hash), and the resultant object conditionally cached within our | 
| 491 |  |  |  |  |  |  | # IBM::StorageSystem object and pushed onto our returned array. | 
| 492 |  |  |  |  |  |  | # | 
| 493 |  |  |  |  |  |  | # Note that this method includes a slight hack to cater for the lack of non-unique per-object | 
| 494 |  |  |  |  |  |  | # keys in CLI output for some object types.  In this scenario it is necessary to create | 
| 495 |  |  |  |  |  |  | # a unique key via composite fields concatenated with a colon. | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | sub __get_sl_objects { | 
| 498 | 0 |  |  | 0 |  |  | my( $self, %args ) = @_; | 
| 499 | 0 |  |  |  |  |  | my @objs = split /\n/, $self->__cmd( $args{ cmd } ); | 
| 500 | 0 |  |  |  |  |  | my @headers = map { s/ /_/g; s/\.//g; $_ } | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | split /:/, shift @objs; | 
| 502 | 0 |  |  |  |  |  | my @res; | 
| 503 |  |  |  |  |  |  |  | 
| 504 | 0 |  |  |  |  |  | foreach my $object ( @objs ) { | 
| 505 | 0 |  |  |  |  |  | my (%a, $c); | 
| 506 |  |  |  |  |  |  |  | 
| 507 | 0 |  |  |  |  |  | foreach my $val ( split /:/, $object ) { | 
| 508 | 0 |  |  |  |  |  | $c++; | 
| 509 | 0 | 0 |  |  |  |  | next if $headers[ $c - 1 ] =~ /^(lsnode|lsexport|lshealth|lsfs|SensorSummary|Share|CtdbHost|HEADER|reserved)$/; | 
| 510 | 0 |  |  |  |  |  | $a{ $headers[ $c - 1 ] } = $val | 
| 511 |  |  |  |  |  |  | } | 
| 512 |  |  |  |  |  |  |  | 
| 513 | 0 | 0 |  |  |  |  | if ( $args{ id } =~ /:/ ) { | 
| 514 | 0 |  |  |  |  |  | my ($nid, $nval); | 
| 515 |  |  |  |  |  |  |  | 
| 516 | 0 |  |  |  |  |  | foreach my $id ( split /:/, $args{ id } ) { | 
| 517 | 0 |  |  |  |  |  | $nid .= ":$id"; $nval .= ":$a{ $id }" | 
|  | 0 |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | } | 
| 519 |  |  |  |  |  |  |  | 
| 520 | 0 |  |  |  |  |  | $nid =~ s/^://; $nval =~ s/^://; | 
|  | 0 |  |  |  |  |  |  | 
| 521 | 0 |  |  |  |  |  | $a{ $nid } = $nval | 
| 522 |  |  |  |  |  |  | } | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 0 |  |  |  |  |  | my $obj = $args{ class }->new( $self, %a ); | 
| 525 | 0 | 0 |  |  |  |  | $self->{ $args{ type } }->{ $a{ $args{ id } } } = $obj unless $args{ nocache }; | 
| 526 | 0 |  |  |  |  |  | push @res, $obj | 
| 527 |  |  |  |  |  |  | } | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | return @res | 
| 530 | 0 |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | # __get_ml_objects - Get Multi-Line Objects | 
| 533 |  |  |  |  |  |  | # This method is used to parse CLI command where detailed information on | 
| 534 |  |  |  |  |  |  | # single object is returned in a columnar format - e.g. | 
| 535 |  |  |  |  |  |  | # | 
| 536 |  |  |  |  |  |  | #	Column_1	Value_1 | 
| 537 |  |  |  |  |  |  | #	Column_2	Value_2 | 
| 538 |  |  |  |  |  |  | #	...		... | 
| 539 |  |  |  |  |  |  | #	Column_N	Value_N | 
| 540 |  |  |  |  |  |  | # | 
| 541 |  |  |  |  |  |  | # To parse this data we split the output on a delimiter (usually whitespace) | 
| 542 |  |  |  |  |  |  | # and treat each row as a name,value hash pair for an attribute of a single object. | 
| 543 |  |  |  |  |  |  | # For example, the above example output would become: | 
| 544 |  |  |  |  |  |  | # | 
| 545 |  |  |  |  |  |  | #	Column_1 => Value_1, | 
| 546 |  |  |  |  |  |  | #	Column_2 => Value_2, | 
| 547 |  |  |  |  |  |  | #	... | 
| 548 |  |  |  |  |  |  | #	Column_N => Value_N, | 
| 549 |  |  |  |  |  |  | # | 
| 550 |  |  |  |  |  |  | # The resultant hash is passed as the argument to the constructor of the type | 
| 551 |  |  |  |  |  |  | # specified by the $args{ class } value and the resultant object is optionally cached | 
| 552 |  |  |  |  |  |  | # in our and pushed onto the return array. | 
| 553 |  |  |  |  |  |  | # | 
| 554 |  |  |  |  |  |  | # Things to note in this sub include the hack required to handle non-unique column names. | 
| 555 |  |  |  |  |  |  | # For example; an 'lsfabric' output may include multiple 'WWPN' columns, so it is | 
| 556 |  |  |  |  |  |  | # neccesary to identify duplicate column names and treat the corresponding hash values | 
| 557 |  |  |  |  |  |  | # as anonymous arrays rather than scalars. | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | sub __get_ml_objects { | 
| 560 | 0 |  |  | 0 |  |  | my ( $self, %args ) = @_; | 
| 561 | 0 |  |  |  |  |  | my @res; | 
| 562 |  |  |  |  |  |  |  | 
| 563 | 0 |  |  |  |  |  | foreach my $object ( @{ $args{ objects } } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 564 | 0 |  |  |  |  |  | my ( %a, %dkeys ); | 
| 565 | 0 |  |  |  |  |  | my @output = split /\n/, ( split /\n\n/, $self->__cmd( "$args{ cmd } $object" ) )[0]; | 
| 566 | 0 |  |  |  |  |  | %dkeys = map { $_ => $dkeys{ $_ }++ } | 
|  | 0 |  |  |  |  |  |  | 
| 567 | 0 |  |  |  |  |  | map { ( split /\s/, $_ )[0] } @output; | 
| 568 |  |  |  |  |  |  |  | 
| 569 | 0 |  |  |  |  |  | for ( @output ) { | 
| 570 | 0 | 0 |  |  |  |  | last if /^\s*$/; | 
| 571 | 0 |  |  |  |  |  | s/$/ -/; | 
| 572 | 0 |  |  |  |  |  | my ( $var, $val ) = ( split /\s/ )[0,1]; | 
| 573 |  |  |  |  |  |  |  | 
| 574 | 0 | 0 |  |  |  |  | if ( $dkeys{ $var } >= 1 ) { | 
| 575 | 0 |  |  |  |  |  | push @{ $a{ $var } }, $val | 
|  | 0 |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | } | 
| 577 |  |  |  |  |  |  | else { | 
| 578 | 0 |  |  |  |  |  | $a{ $var } = $val | 
| 579 |  |  |  |  |  |  | } | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  |  | 
| 582 | 0 |  |  |  |  |  | my $obj = $args{ class }->new( $self, %a ); | 
| 583 | 0 | 0 |  |  |  |  | $self->{ $args{ type } }->{ $a{ $args{ id } } } = $args{ class }->new( $self, %a ) unless ( $args{ nocache } ); | 
| 584 | 0 |  |  |  |  |  | push @res, $obj | 
| 585 |  |  |  |  |  |  | } | 
| 586 |  |  |  |  |  |  |  | 
| 587 |  |  |  |  |  |  | return @res | 
| 588 | 0 |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | 1; | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | __END__ |