# # Copyright (c) 2001-2013 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary ComponentState Module for the "raid_config info listdisk" command ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::RaidConfigInfoListDisk =head1 DESCRIPTION C is a derived class of L. It is a state object encapsulating the information returned by the 7Mode/Nodescope command "raid_config info listdisk" command. =head1 ATTRIBUTES The fields returned in the parsed output of the command are attributes of this CS object. Each of the attributes listed below have accessor methods of the same name. For example, for the attribute "aggregate", this can be done: my $aggregate = $cs_obj->aggregate(); Note that this CS package can dynamically add attributes at run-time, so even if a field of output is not listed below, it can still be used. =over =item C<< command_interface >> =item C<< aggregate >> For disks that have been assigned to an aggregate, this is the aggregate to which the disk has been assigned. For un-assigned disks, this is stored as "-" to indicate "Not Applicable". Filled in for 7Mode/Nodescope CLI =item C<< alt_rightsize >> Filled in for 7Mode/Nodescope CLI =item C<< avl_blks >> Filled in for 7Mode/Nodescope CLI =item C<< avl_mb >> Filled in for 7Mode/Nodescope CLI =item C<< avl_waflblks >> Filled in for 7Mode/Nodescope CLI =item C<< bay >> Filled in for 7Mode/Nodescope CLI =item C<< bps >> Bytes per sector for disk. Possible values - 520, 512. Filled in for 7Mode/Nodescope CLI =item C<< broken >> Filled in for 7Mode/Nodescope CLI =item C<< chan_name >> Channel name on which disk resides. Filled in for 7Mode/Nodescope CLI =item C<< chan_suffix >> Filled in for 7Mode/Nodescope CLI =item C<< chan_suffix2 >> Filled in for 7Mode/Nodescope CLI =item C<< checksum_type >> Checksum type of the disk. Possible values - block, advanced_zoned, zoned. Filled in for 7Mode/Nodescope CLI =item C<< cksum_errcnt >> Filled in for 7Mode/Nodescope CLI =item C<< cluster_name >> The name of the disk in Nodescope and on the NGSH is not the same for FS. This field denotes the NGSH name of the disk. Filled in for Nodescope CLI. =item C<< container_disk_id >> Filled in for 7Mode/Nodescope CLI =item C<< container_type >> Filled in for 7Mode/Nodescope CLI =item C<< copy_progress >> sick disk copy status for the disk. -1 represents no sick disk copy and values greater than -1 represents disk with progress in sick disk copy. Filled in for 7Mode/Nodescope CLI =item C<< copy_target_id >> Target disk for Sick Disk Copy. Filled in for 7Mode/Nodescope CLI =item C<< diagonal_parity_disk >> Filled in for 7Mode/Nodescope CLI =item C<< disk >> Filled in for 7Mode/Nodescope CLI =item C<< disk_id >> Filled in for 7Mode/Nodescope CLI =item C<< disk_rgdn >> Raid group disk number representing the order of disks in a group. Dparity has rgdn as 0 and Parity has rgdn as 1 for Raid_DP. Parity has rgdn 1 for Raid4. All other data disks follow numbers beyond Parity. Filled in for 7Mode/Nodescope CLI =item C<< disk_type >> Storage type of disk. Possible values - FCAL, ATA, SAS, BSAS, MSATA, SSD, SATA Filled in for 7Mode/Nodescope CLI =item C<< do_dr_home_id >> Filled in for 7Mode/Nodescope CLI =item C<< do_home_id >> Filled in for 7Mode/Nodescope CLI =item C<< do_owner_id >> Filled in for 7Mode/Nodescope CLI =item C<< failure_reason >> Filled in for 7Mode/Nodescope CLI =item C<< fdr_pending >> Filled in for 7Mode/Nodescope CLI =item C<< filesys >> Filled in for 7Mode/Nodescope CLI =item C<< fsm_state >> Finite state machine status of disks. Possible values - failed, inuse, zeroed, zeroing, nonzeroed. These values are not case-sensitive. Filled in for 7Mode/Nodescope CLI =item C<< fw_revision >> Filled in for 7Mode/Nodescope CLI =item C<< group_rgid >> Filled in for 7Mode/Nodescope CLI =item C<< host >> Filled in for 7Mode/Nodescope CLI =item C<< host_adapter >> Host bus adapter to which shelf is connected on which disk resides. Filled in for 7Mode/Nodescope CLI =item C<< id >> Filled in for 7Mode/Nodescope CLI =item C<< is_multidisk_carrier >> Filled in for 7Mode/Nodescope CLI =item C<< logical_disk_type >> Possible values: physical_disk; container_disk; partition Filled in for 7Mode/Nodescope CLI =item C<< maint_test >> Filled in for 7Mode/Nodescope CLI =item C<< maintenance >> Filled in for 7Mode/Nodescope CLI =item C<< media_errcnt >> Filled in for 7Mode/Nodescope CLI =item C<< model >> Filled in for 7Mode/Nodescope CLI =item C<< no_parity_vol >> Filled in for 7Mode/Nodescope CLI =item C<< num_partitions >> Possible values: 0 .. 28 Filled in for 7Mode/Nodescope CLI =item C<< offline_ref >> Filled in for 7Mode/Nodescope CLI =item C<< optimized_recons >> optimized recons state of disk. Possible values - 0, 1. 1 represents optimized_recons is in progress. Filled in for 7Mode/Nodescope CLI =item C<< parent_id >> Filled in for 7Mode/Nodescope CLI =item C<< parent_state >> Status of disk in RAID group. Possible values - data, parity and dparity. These values are not case-sensitive. Filled in for 7Mode/Nodescope CLI =item C<< parent_type >> Filled in for 7Mode/Nodescope CLI =item C<< parity_disk >> Filled in for 7Mode/Nodescope CLI =item C<< partition_index >> Possible values: 0 .. 28 Filled in for 7Mode/Nodescope CLI =item C<< path_name1 >> Active path name of disk in dual/single path configuration. Filled in for 7Mode/Nodescope CLI =item C<< path_name2 >> Alternate path name of disk in dual path configuration. Filled in for 7Mode/Nodescope CLI =item C<< pending >> Filled in for 7Mode/Nodescope CLI =item C<< plex_id >> Filled in for 7Mode/Nodescope CLI =item C<< plex_name >> For disks that have been assigned to an aggregate, this is the plex of the aggregate to which the disk has been assigned. For un-assigned disks, this is stored as "-" to indicate "Not Applicable". Filled in for 7Mode/Nodescope CLI =item C<< plex_pid >> Filled in for 7Mode/Nodescope CLI =item C<< pool >> Pool to which disk belongs to. Possible values - 0, 1. Filled in for 7Mode/Nodescope CLI =item C<< prefail >> Filled in for 7Mode/Nodescope CLI =item C<< raid_name >> Fully qualified disk name. This is in the following format for assigned disks: //// For unassigned disks, this is the same as the disk name. Filled in for 7Mode/Nodescope CLI =item C<< raid_size >> Filled in for 7Mode/Nodescope CLI =item C<< raid_type >> Type of RAID. Possible values - raid4, raid_dp, raid_tec These values are not case-sensitive. Filled in for 7Mode/Nodescope CLI =item C<< rawsize >> Filled in for 7Mode/Nodescope CLI =item C<< replace >> replace state of disk. Possible values - 0, 1. 1 represents disk is marked for replace. Filled in for 7Mode/Nodescope CLI =item C<< rg_name >> For disks that have been assigned to an aggregate, this is the raid group of the aggregate to which the disk has been assigned. For un-assigned disks, this is stored as "-" to indicate "Not Applicable". Filled in for 7Mode/Nodescope CLI =item C<< rightsize >> Filled in for 7Mode/Nodescope CLI =item C<< rlw_errcnt >> Filled in for 7Mode/Nodescope CLI =item C<< rpm >> RPM of a disk. Filled in for 7Mode/Nodescope CLI =item C<< serialno >> Serial number of disk. Filled in for 7Mode/Nodescope CLI =item C<< shelf >> Filled in for 7Mode/Nodescope CLI =item C<< size >> Filled in for 7Mode/Nodescope CLI =item C<< spare >> Represents if the disk is a spare. Possible values - 0, 1. 1 represents disk is a spare. Filled in for 7Mode/Nodescope CLI =item C<< tree >> Filled in for 7Mode/Nodescope CLI =item C<< type >> Filled in for 7Mode/Nodescope CLI =item C<< used >> Filled in for 7Mode/Nodescope CLI =item C<< used_blks >> Filled in for 7Mode/Nodescope CLI =item C<< used_sz >> Filled in for 7Mode/Nodescope CLI =item C<< vendor >> Filled in for 7Mode/Nodescope CLI =item C<< waflblks >> Filled in for 7Mode/Nodescope CLI =item C<< wcxt_errcnt >> Filled in for 7Mode/Nodescope CLI =item C<< zero >> Filled in for 7Mode/Nodescope CLI =back =cut package NACL::CS::RaidConfigInfoListDisk; use strict; use warnings; use Params::Validate qw(validate validate_with SCALAR ARRAYREF HASHREF); use NACL::C::StorageDisk; use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::Exceptions::NoElementsFound qw(:try); use base qw(NACL::CS::ComponentState::ONTAP); use Class::MethodMaker [ scalar => 'aggregate', scalar => 'alt_rightsize', scalar => 'avl_blks', scalar => 'avl_mb', scalar => 'avl_waflblks', scalar => 'bay', scalar => 'bps', scalar => 'broken', scalar => 'chan_name', scalar => 'chan_suffix', scalar => 'chan_suffix2', scalar => 'checksum_type', scalar => 'cksum_errcnt', scalar => 'cluster_name', scalar => 'container_disk_id', scalar => 'container_type', scalar => 'copy_progress', scalar => 'copy_target_id', scalar => 'diagonal_parity_disk', scalar => 'disk', scalar => 'disk_id', scalar => 'disk_rgdn', scalar => 'disk_type', scalar => 'do_dr_home_id', scalar => 'do_home_id', scalar => 'do_owner_id', scalar => 'failure_reason', scalar => 'fdr_pending', scalar => 'filesys', scalar => 'fsm_state', scalar => 'fw_revision', scalar => 'group_rgid', scalar => 'host', scalar => 'host_adapter', scalar => 'id', scalar => 'is_multidisk_carrier', scalar => 'logical_disk_type', scalar => 'maint_test', scalar => 'maintenance', scalar => 'media_errcnt', scalar => 'model', scalar => 'name', scalar => 'no_parity_vol', scalar => 'num_partitions', scalar => 'offline_ref', scalar => 'optimized_recons', scalar => 'parent_id', scalar => 'parent_state', scalar => 'parent_type', scalar => 'parity_disk', scalar => 'partition_index', scalar => 'path_name1', scalar => 'path_name2', scalar => 'pending', scalar => 'plex_id', scalar => 'plex_name', scalar => 'plex_pid', scalar => 'pool', scalar => 'prefail', scalar => 'raid_name', scalar => 'raid_size', scalar => 'raid_type', scalar => 'rawsize', scalar => 'replace', scalar => 'rg_name', scalar => 'rightsize', scalar => 'rlw_errcnt', scalar => 'rpm', scalar => 'serialno', scalar => 'shelf', scalar => 'size', scalar => 'spare', scalar => 'tree', scalar => 'type', scalar => 'used', scalar => 'used_blks', scalar => 'used_sz', scalar => 'vendor', scalar => 'waflblks', scalar => 'wcxt_errcnt', scalar => 'zero', scalar => 'recons_progress', scalar => 'root_spare_size', scalar => 'reconstructing', scalar => 'data_spare_size', scalar => 'anti_diagonal_parity_disk', scalar => 'row_parity_disk', ]; =head1 METHODS =head2 fetch my $disk_state = NACL::CS::RaidConfigInfoListDisk->fetch(command_interface=>$ci,...); my @disk_states = NACL::CS::RaidConfigInfoListDisk->fetch(command_interface=>$ci,...); # More advanced examples # Filter for a single disk my $state = NACL::CS::RaidConfigInfoListDisk->fetch( command_interface => $ci, filter => { disk => $disk_name } ); # Filter for multiple disks my @states = NACL::CS::RaidConfigInfoListDisk->fetch( command_interface => $ci, filter => { disk => "$disk_name1|$disk_name2" } ); # Find all spare disks # See the section below on the supported canned_filters my @states = NACL::CS::RaidConfigInfoListDisk->fetch( command_interface => $ci, canned_filters => [qw(spare)] ); # Find all filesystem disks (i.e. those whose fsm_state is "INUSE") my @states = NACL::CS::RaidConfigInfoListDisk->fetch( command_interface => $ci, canned_filters => [qw(filesystem_disk)] ); # Using the advanced filtering capabilities is also possible. Here's # an example showing how to get non-ATA spare disks: my @states = NACL::CS::RaidConfigInfoListDisk->fetch( command_interface => $ci, canned_filters => [qw(spare)], filter => { disk_type => '!ATA' } ); For CMode, this command is run in Nodescope. This defaults to running on node "local". If the command needs to be run on some other node, then the C argument should be used. NACL::CS::RaidConfigInfoListDisk->fetch(%other_opts, 'nodescope-node-name' => $other_node_name); see L Supports 7Mode/Nodescope CLI. =over =item Exceptions =over =item C When there are no disks matching the query specified then this exception will be thrown. =back =back =cut sub fetch { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my @state_objs = $pkg->SUPER::fetch( @args, choices => [ { method => '_fetch_7mode_cli', interface => 'CLI', set => '7Mode|Nodescope', }, ], exception_text => 'No matching disk(s) found' ); $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } ## end sub fetch sub _fetch_7mode_cli { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_backend_validate_spec(); my $apiset = $opts{apiset}; my %api_args = (info => 'listdisk'); my %filter_copy = %{ $opts{filter} }; my @requested_fields_copy = @{ $opts{requested_fields} }; my $deleted_filter = $pkg->_remove_relational_regex_filters( filter => \%filter_copy, requested_fields => \@requested_fields_copy ); foreach my $field (qw(disk path_name1 path_name2 cluster_name)) { if ( defined $filter_copy{$field} ) { $api_args{info} .= " " . $filter_copy{$field}; last; } } if ($opts{command_interface}->is_cmode() && defined $opts{'nodescope-node-name'}) { $api_args{'nodescope-node-name'} = $opts{'nodescope-node-name'}; } my ($response, $caught_exception); try { $response = $apiset->raid_config(%api_args); } catch NACL::APISet::Exceptions::InvalidNodescopeNodeException with { # Keep this first. This also sub-classes InvalidParamValueException # so should be kept first. We want this case to result in an # exception. $Log->exit() if $may_exit; $_[0]->throw(); } catch NACL::APISet::Exceptions::InvalidParamValueException with { # If we reached here, then the disk name is invalid. $caught_exception = 1; }; if ($caught_exception) { $Log->exit() if $may_exit; return; } my $parsed_output = $response->get_parsed_output(); my @state_objs; foreach my $row (@$parsed_output) { my $obj = $pkg->new(command_interface => $opts{command_interface}); $obj->_set_fields(row => $row, is_zapi => 0, add_unknown_fields => 1, need_to_quote => 0); # "name" is what the APISet parser returns, but we'd like to store # it as "disk" here for clarity. $obj->disk($row->{name}); # If the disk has been assigned to an aggregate, then the raid_name # field is of the form: # /aggr_name/plex_name/rg_name/disk_name # We extract these values and set it in the object. # For spare disks, the raid_name field is simply the same as the # disk name, so in such cases we set the values of aggregate, plex and # rg name to "-" to indicate "not applicable". my ($aggregate, $plex_name, $rg_name); my $raid_name = $row->{raid_name}; if ($raid_name =~ m!/(.*)/(.*)/(.*)/.*!) { $aggregate = $1; $plex_name = $2; $rg_name = "/$1/$2/$3"; } else { $aggregate = '-'; $plex_name = '-'; $rg_name = '-'; } $obj->aggregate($aggregate); $obj->plex_name($plex_name); $obj->rg_name($rg_name); push @state_objs, $obj; } $Log->exit() if $may_exit; return @state_objs; } =head1 CANNED FILTERS See http://brewery.netapp.com/Brewery/QA/TestAutomation/NACL/UsersGuide/ComponentLayerUsersGuide/index.html#canned-filters for a description of canned filters. =head2 spare Returns all spare disks =head2 not_spare Returns all non-spare disks =head2 filesystem_disk Returns all filesystem disks, i.e. those whose fsm_state is "INUSE". =cut sub _canned_filter_spare { return {spare => 1}; } sub _canned_filter_not_spare { return {spare => 0}; } sub _canned_filter_filesystem_disk { return {fsm_state => 'INUSE'}; } # The output of raid_config returns these fields in upper-case, but we're # fine with the filter being either upper or lower case. This is because # (to take fsm_state as example), an fsm_state of "INUSE" would mean # the same as "inuse". sub _case_insensitive_attributes { return [qw(fsm_state parent_state raid_type vendor)]; } sub _apply_filter { $Log->enter() if $may_enter; my ( $pkg, @args ) = @_; my %opts = validate_with( params => \@args, spec => { state_objs => { type => ARRAYREF }, filter => { type => HASHREF, default => {} }, }, allow_extra => 1 ); # Look at all of disk, path_name1, path_name2, cluster_name to check # if filter->{disk} matched any of them. my $disk = $opts{filter}{disk}; if ( defined $disk ) { # Filtering for disk is done here. Delete it so base class does # not attempt it. my %filter = %{ $opts{filter} }; delete $filter{disk}; $opts{filter} = \%filter; my @new_state_objs; my $has_sp_chr = $pkg->_check_relational_regex_filter( filter_value => $disk ); foreach my $cs ( @{ $opts{state_objs} } ) { foreach my $field (qw(disk path_name1 path_name2 cluster_name)) { my $cs_value = $cs->$field(); if ( defined $cs_value ) { if ($has_sp_chr) { my $matched = $pkg->_apply_relational_regex_filters( filter => { $field => $disk }, state_objs => [$cs] ); if ($matched) { push @new_state_objs, $cs; last; } } elsif ( $disk eq $cs_value ) { push @new_state_objs, $cs; last; } } } } @{$opts{state_objs}} = @new_state_objs; } $pkg->SUPER::_apply_filter(%opts); $Log->exit() if $may_exit; return @{ $opts{state_objs} }; } sub _fetch_validate_spec { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %spec = ( %{$pkg->SUPER::_fetch_validate_spec(@args)}, unknown_fields_added_by_backend => {type => SCALAR, default => 1}, ); $Log->exit() if $may_exit; return {%spec}; } 1;