# # Copyright (c) 2014 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Inode ## @author weili@netapp.com ## @status shared ## @pod here =head1 NAME NACL::MTask::Inode =head1 SYNOPSIS use NACL::MTask::Inode my $inode_obj = NACL::MTask::Inode->new( command_interface => $ci, ); $inode_obj->set_larger_larger_inode_flag(inode_version=>4); =head1 DESCRIPTION C provides methods related to inode. =over =back =cut package NACL::MTask::Inode; use strict; use warnings; use base qw(NACL::MTask::MTask); use Params::Validate qw(validate_with CODEREF OBJECT SCALAR ARRAYREF BOOLEAN HASHREF); use NATE::BaseException qw(:try); use NACL::ComponentUtils qw(Dumper); use NACL::Cleanup; use NATE::Log qw(log_global); use NACL::MTask::VolumeFileIterator::ZapiFile; use Data::Dumper; my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Class::MethodMaker [ scalar => [{'-type' => 'NACL::C::Node'}, 'command_interface'], scalar => [{'-type' => 'NACL::C::Volume'}, 'volume'], ]; =head1 NAME new =head1 DESCRIPTION C provides methods related to inode =head1 SYNOPSIS use NACL::MTask::Inode my $inode_obj = NACL::MTask::Inode->new( command_interface => $ci, ); =over =head1 ATTRIBUTES =item C<< command_interface >> (Required, isa "NACL::C::Node") A command interface object which represents a Node to communicate with in order to send commands to a filer. =over =cut sub new { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ignore_primary_keys => 1, ); # Now create the Perl Object by blessing the hash my $self = bless(\%opts, $pkg); $Log->debug('Object created:' . Dumper($self)); $Log->exit() if $may_exit; return $self; } ## end sub new =head1 NAME set_larger_larger_inode_flag =head1 DESCRIPTION Set wafl flag to make volume created will be v3/v4 inode volume $inode_obj->set_larger_larger_inode_flag(inode_version=>3); =over =head1 ATTRIBUTES =item C<< inode_version >> (Optional) to create a v3/v4 volume. i node_version => 3: volume created will be a v3 volume node_version => 4: volume created will be a v4 volume (default) 4 =over =cut sub set_larger_larger_inode_flag { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => {inode_version => {type => SCALAR, optional => 1, default => 4},}, ); my $node = $opts{'command_interface'}; my $inode_version = $opts{'inode_version'}; my $node_api = $node->get_7m_or_nodescope_apiset(); ## modify after setflags is available if ($inode_version == 3) { $Log->comment('set wafl flag to for creating v3 vlume'); $node_api->setflag( flag => "wafl_larger_larger_inodes_enable", value => 0 ); $node_api->setflag( flag => "wafl_v4ino_upgrade_disable", value => 1 ); } else { $Log->comment('set wafl flag to for creating v4 vlume'); $node_api->setflag( flag => "wafl_larger_larger_inodes_enable", value => 1 ); $Log->comment('Set wafl_v4ino_upgrade_disable to 0'); $node_api->setflag( flag => "wafl_v4ino_upgrade_disable", value => 0 ); } $Log->exit() if $may_exit; } ## end sub set_larger_larger_inode_flag =head1 NAME get_icache_inode_reclaims_and_icache_phases =head1 DESCRIPTION Get value "icache_inode_reclaims" and "icache_phases" from wafl_susp my ($icache_inode_reclaims, $icache_phases) = $inode_obj->get_icache_inode_reclaims_and_icache_phases(); =over =cut sub get_icache_inode_reclaims_and_icache_phases { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with(params => \@_,); my $node = $opts{command_interface}; my $node_api = $node->get_7m_or_nodescope_apiset(); $Log->comment( 'get wafl stats for icache_phases and icache_inode_reclaims'); ## TODO: will use wafl_susp after NACL burt 852784 is fixed ## file a burt for Node builder to have wafl_susp my $wafl_susp = $node_api->execute_command( command => "wafl_susp -w", 'privilege-level' => "test" ); $wafl_susp =~ /icache_inode_reclaims\s*=\s*(\d+)/i; my $icache_inode_reclaims = $1; $wafl_susp =~ /icache_phases\s*=\s*(\d+)/i; my $icache_phases = $1; $Log->exit() if $may_exit; return ($icache_inode_reclaims, $icache_phases); } ## end sub get_icache_inode_reclaims_and_icache_phases =head1 NAME wait_for_inode_upgrade_status =head1 DESCRIPTION waiting for inode upgrade status to reach the given status $inode_obj->wait_for_inode_upgrade_status( volume => $vol, vserver => $vs, status => 'scanning' ); =over =head1 ATTRIBUTES =item C<< volume >> (Required) NACL C volume object =item C<< vserver >> (Required) vserver =item C<< status >> (Required) inode upgrade status to wait for =item C<< timeout >> (Optional) waiting time out Default: 3600 =over =cut sub wait_for_inode_upgrade_status { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { aggregate => {type => 'NACL::C::Aggregate', optional => 1}, volume => {type => 'NACL::C::Volume', optional => 1}, vserver => {type => SCALAR, optional => 1}, status => {type => SCALAR, optional => 0}, timeout => {type => SCALAR, default => 3600}, }, ); my $node = $opts{command_interface}; my $aggr = $opts{aggregate}; my $vol = $opts{volume}; my ($vserver, $vol_name, $aggr_name); if (defined $vol) { $vserver = $opts{vserver}?$opts{vserver}:$vol->get_one_state_attribute('vserver'); } my $status = $opts{status}; my $timeout = $opts{timeout}; my $interval = $opts{interval}; my $node_api = $node->get_7m_or_nodescope_apiset(); $Log->comment("Waiting for v4ino upgrade to status $status "); my $command; if (defined $vol ){ $vol_name = $vol->volume; $command = "set test; volume inode-upgrade show -vserver $vserver -volume $vol_name -fields status"; } else{ $aggr_name = $aggr->aggregate; $command = "aggregate inode-upgrade show -aggregate $aggr_name -fields status"; } $node_api->execute_raw_command(command => "set test -confirmation off"); my $response; my $done; my $end_time = NATE::Time::timeout2time($timeout); while (time < $end_time) { $Log->comment("Waiting for v4ino upgrade reach to $status"); $response = $node_api->execute_raw_command(command => $command,); if ($response =~ /$status\s/) { $done = 1; $Log->debug("status reached"); last; } else { $Log->debug("status not reached"); } } if (!$done) { NACL::Exceptions::Timeout->throw( "Upgrade did not complete in $timeout seconds"); } $Log->exit() if $may_exit; } ## end sub wait_for_inode_upgrade_status =head1 NAME process_files_recursively =head1 DESCRIPTION Process files through file tree $inode_obj->process_files_recursively( canned_callback_method => "\&create_acls_streams", base_dir_path => "Qtree", volume => $vol, callback_args => { vol_name => $vol_name, vs => $vs, client_apiset => $client_api, v4t_setacl => $v4t_setacl, acl_path => $acl_path, file_size => $file_size, mount_ip => $mount_ip, stream_count => 2, nacltask_wait => 0 } ); $inode_obj->process_files_recursively( canned_callback_method => "access_acls_streams", base_dir_path => "Qtree", volume => $vol, callback_args => { client_apiset => $client_api, vs => $vs, vol_name => $vol_name, mount_point => $mount->mount_point, getfacl => $getfacl, } ); =over =head1 ATTRIBUTES =item C<< canned_callback_method >> (Required) callback function for file operation while loop through the file tree canned_callback_method => "create_acls_streams": call sub create_acls_streams while loop through the file tree =item C<< base_dir_path >> (Required) relative path of the top directory. for /vol/vol1/qree1/dir1, relative path is: relative_path => "qree1/dir1" =item C<< volume >> ((Required, isa "NACL::STask::Volume"), volume object =item C<< callback_args >> ((Required, hash reference), callback function args =item C<< file_obj >> ((Optional, isa "NACL::MTask::VolumeFileIterator::ZapiFile") =back =over =cut sub process_files_recursively { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { canned_callback_method => {type => SCALAR, optional => 0}, base_dir_path => {type => SCALAR, optional => 0}, file_obj => {type => SCALAR, optional => 1}, volume => {type => 'NACL::STask::Volume', optional => 0}, callback_args => {type => HASHREF}, }, ); my $canned_callback_method = $opts{'canned_callback_method'}; my $base_dir_path = $opts{'base_dir_path'}; my $vol = $opts{'volume'}; my $callback_args = $opts{'callback_args'}; my $file_obj = $opts{'file_obj'}; $file_obj ||= NACL::MTask::VolumeFileIterator::ZapiFile->new( path => $base_dir_path, volume => $vol, ); $callback_args->{'$file_obj'} = $file_obj; $callback_args->{'base_dir_path'} = $base_dir_path; my @files = grep { $_->{_name} !~ /^\./ } $file_obj->enumerate_directory(); no strict 'refs'; foreach my $file_obj (@files) { if ($file_obj->_type =~ /directory/) { ## recursion $opts{'base_dir_path'} = $file_obj->raw_name; $pkg->process_files_recursively(%opts); } else { $callback_args->{'file_obj'} = $file_obj; $pkg->$canned_callback_method(%{$callback_args}); } } $Log->exit() if $may_exit; } ## end sub process_files_recursively =head1 NAME create_acls_streams =head1 DESCRIPTION create acls and streams on a file $inode_obj->create_acls_streams( file_obj => $file_obj, client_apiset => $client->apiset(), vs => $vs, vol_name => "vol1", v4t_setacl => $v4t_setacl, acl_path => $acl_path, file_size => '10k', mount_ip => $mount_ip, stream_count => 2, ); =over =head1 ATTRIBUTES =item C<< file_obj >> (Required) file object of the file to be created, isa "NACL::MTask::VolumeFileIterator::ZapiFile =item C<< client_apiset >> (Required), client api object =item C<< vol_name >> ((Required, isa "NACL::STask::Volume"), volume object =item C<< mount_ip >> ((Required), mount ip address =item C<< vserver >> (Optional) vserver name =item C<< v4t_setacl >> (Optional) v4t_setacl command path: v4t_setacl => "/usr/software/test/bin/v4t/v4t_setacl" (Default)"/usr/software/test/bin/v4t/v4t_setacl" =item C<< acl_path >> (Optional) acl file path: acl_path => "/usr/software/test/bin/v4t/test_files/acl_input_file2" (Default) "/usr/software/test/bin/v4t/test_files/acl_input_file2" =item C<< file_size >> (Optional) file_size file_size => "10k" =item C<< stream_count >> (Optional) number of streams to create on each file stream_count => 10 (Default) 1 =item C<< file >> (Required), file name =back =over =cut sub create_acls_streams { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { file_obj => {type => SCALAR, optional => 0}, client_apiset => {type => SCALAR, optional => 0}, vserver => {type => SCALAR, optional => 0}, vol_name => {type => SCALAR, optional => 0}, stream_name => {type => SCALAR, default => "stream"}, v4t_setacl => { type => SCALAR, optional => 1, default => "/usr/software/test/bin/v4t/v4t_setacl" }, acl_path => { type => SCALAR, default => "/usr/software/test/bin/v4t/test_files/acl_input_file2" }, file_size => {type => SCALAR, default => '10k'}, mount_ip => {type => SCALAR, optional => 0}, stream_count => {type => SCALAR, default => 1}, nacltask_wait => {type => SCALAR, optional => 1}, }, allow_extra => 1, ); my $file_name = $opts{file_obj}->raw_name; foreach my $j (1 .. $opts{stream_count}) { $Log->debug("create stream on $opts{dblade_file_name} "); NACL::STask::VolumeFile->create( command_interface => $opts{command_interface}, path => "/vol/" . $opts{vol_name} . "/" . $file_name, vserver => $opts{vserver}, 'stream-name' => $opts{stream_name} . $j, size => $opts{file_size}, nacltask_wait => $opts{nacltask_wait}, ); } $opts{client_apiset}->execute_raw_command(command => "sudo $opts{v4t_setacl} -I $opts{'mount_ip'} -C $opts{vol_name} -F $file_name -f $opts{acl_path}" ); $Log->exit() if $may_exit; } ## end sub create_acls_streams =head1 NAME access_acls_streams =head1 DESCRIPTION Access acls and streams of a file $inode_obj->access_acls_streams( file_obj => $file_obj, client_apiset => $client_api, vserver => $vs, vol_name => "vol1", mount_point => $mount->mount_point, getfacl => $getfacl, ); =over =head1 ATTRIBUTES =item C<< file_obj >> (Required) file object, isa "NACL::MTask::VolumeFileIterator::ZapiFile =item C<< client_apiset >> (Required), client api object =item C<< vol_name >> (Required), volume name =item C<< file >> (Required), file name =item C<< vserver >> (Reauired) vserver name =item C<< getfacl >> (Optional) getfacl command path: getfacl => "/usr/bin/getfacl" (default) "/usr/bin/getfacl" =back =over =cut sub access_acls_streams { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { file_obj => {type => SCALAR, optional => 0}, client_apiset => {type => SCALAR, optional => 0}, vserver => {type => SCALAR, optional => 0}, vol_name => {type => SCALAR, optional => 0}, mount_point => {type => SCALAR, optional => 0}, getfacl => { type => SCALAR, default => "/usr/bin/getfacl" }, }, allow_extra => 1, ); my $node = $opts{command_interface}; my $file_name = $opts{file_obj}->raw_name; my $path = $opts{mount_point} . "/" . $file_name; $Log->debug("start _access_streams on $path"); $Log->debug("list system shell stuff"); my $cmd = "sudo ls -al /clus/$opts{vserver}/$opts{vol_name}/$file_name/nmatr\%"; my $output = $node->get_systemshell_apiset->execute_raw_command(command => $cmd); if ( $output =~ /Not a directory/i || $output =~ /No such file or directory/i) { return; } $opts{client_apiset} ->execute_raw_command(command => "sudo $opts{getfacl} $path"); $Log->exit() if $may_exit; } ## end sub access_acls_streams =head1 NAME create_acls =head1 DESCRIPTION Create acls, can be used as modify acls by re-creating it $inode_obj->process_files_recursively( canned_callback_method => "create_acls", base_dir_path => "Qtree", volume => $vol, callback_args => { vol_name => $vol_name, client_apiset => $client_api, v4t_setacl => $v4t_setacl, acl_path => $acl_path, mount_ip => $mount_ip, } ); =over =head1 ATTRIBUTES =item C<< file_obj >> (Required) file object, isa "NACL::MTask::VolumeFileIterator::ZapiFile =item C<< client_apiset >> (Required), client api object =item C<< vol_name >> (Required), volume name =item C<< mount_ip >> ((Required), mount ip address =item C<< v4t_setacl >> (Optional) v4t_setacl command path: v4t_setacl => "/usr/software/test/bin/v4t/v4t_setacl" (Default)"/usr/software/test/bin/v4t/v4t_setacl" =item C<< acl_path >> (Optional) acl file path: acl_path => "/usr/software/test/bin/v4t/test_files/acl_input_file2" (Default) "/usr/software/test/bin/v4t/test_files/acl_input_file2" =back =over =cut sub create_acls { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { file_obj => {type => SCALAR, optional => 0}, client_apiset => {type => SCALAR, optional => 0}, vol_name => {type => SCALAR, optional => 0}, v4t_setacl => { type => SCALAR, optional => 1, default => "/usr/software/test/bin/v4t/v4t_setacl" }, acl_path => { type => SCALAR, default => "/usr/software/test/bin/v4t/test_files/acl_input_file2" }, mount_ip => {type => SCALAR, optional => 0}, }, allow_extra => 1, ); my $file_name = $opts{file_obj}->raw_name; $opts{client_apiset}->execute_raw_command(command => "sudo $opts{v4t_setacl} -I $opts{'mount_ip'} -C $opts{vol_name} -F $file_name -f $opts{acl_path}" ); $Log->exit() if $may_exit; } ## end sub create_acls =head1 NAME delete_streams =head1 DESCRIPTION Delete all streams of a file my @streams = $inode_obj->process_files_recursively( canned_callback_method => "delete_streams", base_dir_path => "Qtree", volume => $vol, callback_args => { vol_name => $vol_name, vserver => $vs, } ); =over =head1 ATTRIBUTES =item C<< file_obj >> (Required) file object, isa "NACL::MTask::VolumeFileIterator::ZapiFile =item C<< vol_name >> (Required), volume name =item C<< vserver >> (Reauired) vserver name =back =over =cut sub delete_streams { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { file_obj => {type => SCALAR, optional => 0}, vserver => {type => SCALAR, optional => 0}, vol_name => {type => SCALAR, optional => 0}, }, allow_extra => 1, ); my $file_name = $opts{file_obj}->raw_name; my $node = $opts{command_interface}; $Log->debug("rm stream for $file_name"); my @streams = $pkg->_get_streams( file_obj => $opts{file_obj}, vol_name => $opts{vol_name}, ); my $cmd_path = "sudo rm /clus/$opts{vserver}/$opts{vol_name}/$file_name/nmatr\%/"; foreach my $stream (@streams) { $node->get_systemshell_apiset->execute_raw_command( command => $cmd_path . "$stream"); } $Log->exit() if $may_exit; } ## end sub delete_streams =head1 NAME rename_streams =head1 DESCRIPTION Rename all streams of a file my @streams = $inode_obj->process_files_recursively( canned_callback_method => "rename_streams", base_dir_path => "Qtree", volume => $vol, callback_args => { vol_name => $vol_name, vserver => $vs, } ); =over =head1 ATTRIBUTES =item C<< file_obj >> (Required) file object, isa "NACL::MTask::VolumeFileIterator::ZapiFile =item C<< vol_name >> (Required), volume name =item C<< vserver >> (Reauired) vserver name =item C<< new_stream_name >> (Optional) new stream name, default: new_+old stream name =back =over =cut sub rename_streams { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { file_obj => {type => SCALAR, optional => 0}, vserver => {type => SCALAR, optional => 0}, vol_name => {type => SCALAR, optional => 0}, new_stream_name => {type => SCALAR, default => "new_"}, }, allow_extra => 1, ); my $file_name = $opts{file_obj}->raw_name; my $node = $opts{command_interface}; $Log->debug("rename stream for $file_name"); my @streams = $pkg->_get_streams( file_obj => $opts{file_obj}, vol_name => $opts{vol_name}, ); my $cmd_path = "sudo mv /clus/$opts{vserver}/$opts{vol_name}/$file_name/nmatr\%/"; my $new_name_path = " /clus/$opts{vserver}/$opts{vol_name}/$file_name/nmatr\%/"; foreach my $stream (@streams) { my $cmd = $cmd_path . "$stream" . $new_name_path . $opts{new_stream_name} . $stream; $node->get_systemshell_apiset->execute_raw_command(command => $cmd); } $Log->exit() if $may_exit; } ## end sub rename_streams =head1 NAME trunc_streams =head1 DESCRIPTION Truncate all streams of a file my @streams = $inode_obj->process_files_recursively( canned_callback_method => "trunc_streams", base_dir_path => "Qtree", volume => $vol, callback_args => { vol_name => $vol_name, vserver => $vs, } ); =over =head1 ATTRIBUTES =item C<< file_obj >> (Required) file object, isa "NACL::MTask::VolumeFileIterator::ZapiFile =item C<< vol_name >> (Required), volume name =item C<< vserver >> (Reauired) vserver name =back =over =cut sub trunc_streams { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { file_obj => {type => SCALAR, optional => 0}, vserver => {type => SCALAR, optional => 0}, vol_name => {type => SCALAR, optional => 0}, }, allow_extra => 1, ); my $file_name = $opts{file_obj}->raw_name; my $node = $opts{command_interface}; $Log->debug("truncate stream for $file_name"); my @streams = $pkg->_get_streams( file_obj => $opts{file_obj}, vol_name => $opts{vol_name}, ); my $cmd_path = "/clus/$opts{vserver}/$opts{vol_name}/$file_name/nmatr\%/"; foreach my $stream (@streams) { $node->get_systemshell_apiset->execute_raw_command( command => "sudo chmod 777 " . $cmd_path . $stream); $node->get_systemshell_apiset->execute_raw_command( command => "sudo > " . $cmd_path . $stream); } $Log->exit() if $may_exit; } ## end sub trunc_streams =head1 NAME set_fiji =head1 DESCRIPTION This will activate the FIJI on inode $inode_obj->set_fiji( filter => "\'( dsa.ses.receive (true) (return 1))\'" ); =over =head1 ATTRIBUTES =item C<< filter => string >> The actual fiji program, with sprintf-like format descriptors where arguments should be substituted. =back =over =cut sub set_fiji { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { filter => {type => SCALAR}, }, allow_extra => 1, ); my $command_interface = $opts{command_interface} || $pkg_or_obj->command_interface() ; my $filter = $opts{filter}; NACL::STask::Fiji->set( command_interface => $opts{command_interface}, tag => 'set fiji', program => $filter, ); $Log->exit() if $may_exit; } ## end sub set_fiji ## interal method ## ## get all stream of a file sub _get_streams { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { file_obj => {type => SCALAR, optional => 0}, vol_name => {type => SCALAR, optional => 0}, }, allow_extra => 1, ); my $node = $opts{'command_interface'}; my $node_api = $node->get_7m_or_nodescope_apiset(); my $file_name = $opts{file_obj}->raw_name; my $path = "/vol/" . $opts{vol_name} . "/" . $file_name; my $slist = $node_api->slist('path' => $path)->get_parsed_output()->[0] ->{'extended-inode-info'}; my @streams; foreach my $list (@{$slist}) { push(@streams, $list->{'name'}); } $Log->debug("list of streams:\n" . Dumper(@streams)); $Log->exit() if $may_exit; return @streams; } ## end sub _get_streams 1;