# Copyright (c) 2015 Netapp Inc. ## @summary Granset library to ease using the dblade granset command with files ## @author roopeshv ## @status shared ## @description ## Provides convenience methods over dblade commands. Let's the user avoid ## manually provide inum and generation ids of files in certain cases. ## @synopsis ## my $granset = NACL::STask::Granset->create( ## command_interface => $Node, ## access => 2, ## files => [$file], ## ); ## $granset->show(); ## $granset->item_add(file => $other_file); ## $granset->item_delete(file => $other_file); ## $granset->item_is_dp(file => $file); ## package NACL::STask::Granset; use strict; use warnings; use NACL::ComponentUtils 'Dumper'; use base qw(NACL::C::Component::ONTAP NACL::STask::STask); use Params::Validate qw(SCALAR ARRAYREF); use NACL::C::Volume; use Class::MethodMaker [ scalar => 'granset_id', scalar => 'volume', scalar => 'command_interface', scalar => [qw(_volume_name _apiset _volume_node)] ]; =pod =head1 METHODS =head2 create Creates a new granset with given access on all the files provided my $granset = NACL::STask::Granset->create( command_interface => $node, access => 1, files => [@files], ); =head3 Options =over =item C<< command_interface => $node >> (required) Command interface over which the commands are executed =item C<< uuid => $uuid >> (optional) A unique identifier, usually Data::UUID can be used or uuidgen from command line =item C<< access => $access_value >> (optional) Access permissions. Usually WAFL_GS_RO, WAFL_GS_RW, 1 or 2 =item C<< files => \@files >> (optional) List of files of L<> type =back =cut sub create { my ($pkg, @args) = @_; my %opts = $pkg->_common_validate_with( params => \@args, additional_spec => { uuid => {optional => 1}, access => {optional => 0}, files => {type => ARRAYREF, optional => 0}, }, allow_extra => 1, ); my $uuid = $opts{uuid} || Data::UUID->new()->create_str(); my @files = @{$opts{files}}; my $vol = $files[0]->get_containing_volume_obj(); my $command_interface = $opts{command_interface} || $vol->command_interface; my $node = $vol->get_local_node_obj(); my $apiset = $node->get_7m_or_nodescope_apiset(); my $volname = $vol->volume(); my (@file_ids, @generation_ids); foreach my $file (@files) { my $inode_explore = _inode_explore($file); push @generation_ids, $inode_explore->generation; push @file_ids, $inode_explore->inum; } my $resp = $apiset->granset_create( gs_uuid => $uuid, gs_access => $opts{access}, file_ids => join(",", @file_ids), generation_ids => join(",", @generation_ids), item_count => scalar @files, volume => $volname, ); my $granset_id = $resp->get_parsed_output->{granset_id}; return $pkg->new( command_interface => $command_interface, granset_id => $granset_id, volume => $vol, ); } =pod =head2 modify Modify an existing granse $granset->modify( access => 1, state => 2, redirect_snapid => 1, ); =head3 Options =over =item C<< access => $access_value >> (optional) Access permissions. Usually WAFL_GS_RO, WAFL_GS_RW, 1 or 2 =item C<< state => $state >> (optional) State value of granset. Usually GS_STATE_VALID =back =cut sub modify { my ($pkg_or_obj, @args) = @_; $pkg_or_obj->_verify_invocation(style => 'instance_only'); my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { access => {optional => 0}, state => {optional => 0}, redirect_snapid => {optional => 0}, }, ); my ($apiset, $volname, $gsid); $gsid = $pkg_or_obj->granset_id; $apiset = $pkg_or_obj->volume()->get_local_node_obj() ->get_7m_or_nodescope_apiset(); $volname = $pkg_or_obj->volume()->volume(); # ideally, we need to return parsed_output. Postpoining, until we have logs # for it $apiset->granset_modify( gsid => $gsid, gs_access => $opts{access}, gs_state => $opts{state}, gs_redirect_snapid => $opts{redirect_snapid}, volume => $volname ); return; } =pod =head2 show (instance method) Gets the granset details for an object. The return value contains the file ids and generations of the granset and other properties of the granset. =cut sub show { my ($pkg_or_obj, @args) = @_; $pkg_or_obj->_verify_invocation(style => 'instance_only'); my $gsid = $pkg_or_obj->granset_id; my $volname = $pkg_or_obj->volume()->volume(); my $apiset = $pkg_or_obj->volume()->get_local_node_obj() ->get_7m_or_nodescope_apiset(); return $apiset->granset_show( gsid => $gsid, volume => $volname )->get_parsed_output; } =pod =head2 item_add, item_delete (instance method) item_add adds a file to granset (instance method) item_delete removes a file from granset =head3 Options =over =item C<< file => $file >> File object of type L<> =back =cut sub _item_add_or_delete { my ($pkg_or_obj, $method, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => {file => {optional => 0},}, allow_extra => 1, ); my $file = $opts{file}; my ($volname, $node, $apiset); # convenience assignment $node = $pkg_or_obj->volume()->get_local_node_obj(); $apiset = $node->get_7m_or_nodescope_apiset(); $volname = $pkg_or_obj->volume()->volume(); my $file_explore = _inode_explore($file); my $generation_id = $file_explore->generation; my $file_id = $file_explore->inum; return $apiset->$method( gsid => $pkg_or_obj->granset_id, item_count => 1, file_ids => $file_id, generation_ids => $generation_id, volume => $volname, )->get_parsed_output(); } sub item_add { my ($pkg_or_obj, @args) = @_; $pkg_or_obj->_verify_invocation(style => 'instance_only'); return $pkg_or_obj->item_add_or_delete('granset_item_add', @args); } sub item_delete { my ($pkg_or_obj, @args) = @_; $pkg_or_obj->_verify_invocation(style => 'instance_only'); return $pkg_or_obj->item_add_or_delete('granset_item_delete', @args); } sub lookup { my ($pkg_or_obj, @args) = @_; $pkg_or_obj->_verify_invocation(style => 'instance_only'); my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => {uuid => {optional => 0}}, allow_extra => 1, ); my $volume = $pkg_or_obj->volume(); my $volname = $volume->volume(); my $node = $volume->get_local_node_obj(); my $apiset = $node->get_7m_or_nodescope_apiset(); return $apiset->granset_lookup( gs_uuid => $opts{uuid}, volume => $volname )->get_parsed_output; } =pod =head2 item_is_dp (instance method) For a given file, tells if it's DP or non-DP item =head3 options =over =item C<< file => $file >> (required) File, for which DP value is retrieved =back =cut sub item_is_dp { my ($pkg_or_obj, @args) = @_; $pkg_or_obj->_verify_invocation(style => 'instance_only'); my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { file => {optional => 0}, volume => {optional => 1}, }, allow_extra => 1, ); my $file = $opts{file}; my ($volume, $volname, $node, $apiset); if ($opts{volume}) { $volume = $opts{volume}; $node = $volume->get_local_node_obj(); $apiset = $node->get_7m_or_nodescope_apiset(); $volname = $volume->volume(); } else { $volume = $pkg_or_obj->volume(); $node = $volume->get_local_node_obj(); $apiset = $node->get_7m_or_nodescope_apiset(); $volname = $volume->volume(); } my $command_interface = $opts{command_interface} || $pkg_or_obj->command_interface(); my $file_explore = _inode_explore($file); my $generation_id = $file_explore->generation; my $file_id = $file_explore->inum; return $apiset->granset_item_is_dp( fileid => $file_id, generation => $generation_id, volume => $volname, )->get_parsed_output; } =pod =head2 purge (instance method) Deletes the granset =cut sub purge { my ($pkg_or_obj, @args) = @_; $pkg_or_obj->_verify_invocation(style => 'instance_only'); my $volume = $pkg_or_obj->volume(); my $node = $volume->get_local_node_obj(); my $apiset = $node->get_7m_or_nodescope_apiset(); my $volname = $volume->volume(); return $apiset->granset_delete( gsid => $pkg_or_obj->granset_id, volume => $volname, )->get_parsed_output; } sub metafile_create { my ($pkg_or_obj, @args) = @_; return $pkg_or_obj->metafile_method('create', @args); } sub metafile_delete { my ($pkg_or_obj, @args) = @_; return $pkg_or_obj->metafile_method('delete', @args); } sub metafile_dump { my ($pkg_or_obj, @args) = @_; return $pkg_or_obj->metafile_method('dump', @args); } sub metafile_load { my ($pkg_or_obj, @args) = @_; return $pkg_or_obj->metafile_method('load', @args); } sub _metafile_method { my ($pkg_or_obj, $method, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => {volume => {optional => 1},}, allow_extra => 1, ); my ($apiset, $volname); if ($opts{volume}) { my $vol = $opts{volume}; $apiset = $vol->get_local_node_obj()->get_7m_or_nodescope_apiset(); $volname = $vol->volume(); } else { $apiset = $pkg_or_obj->volume()->get_local_node_obj()->get_7m_or_nodescope_apiset(); $volname = $pkg_or_obj->volume->volume(); } my $_method = "metafile_$method"; return $apiset->$_method(volume => $volname)->get_parsed_output; } sub _inode_explore { my $file = shift; my $filepath = $file->path(); # split into following format # '/vol/vname/dir/path' => ('', 'vol', 'vname', 'dir/path'); my ($empty, $volstr, $vname, $rel_filepath) = split("/", $filepath, 4); return NACL::STask::Volume->explore( command_interface => $file->command_interface(), format => 'inode', find => 'type!=0', scope => $vname . ".64/$rel_filepath", ); } 1;