# $Id$ # # Copyright (c) 2001-2013 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary VsimSnapshot Task Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::STask::VsimSnapshot =head1 DESCRIPTION C provide methods to vsim snapshot functionality on Linux/solaris Host. This Task support these functionality: ------------------------------------------------------------ | Commands | Task Method | ----------------------------------------------------------- | vsim snap | NACL::STask::VsimSnapshot->create() | | vsim snapback | NACL::STask::VsimSnapshot->restore() | | vsim snapdelete | NACL::STask::VsimSnapshot->delete() | | vsim snaplist | NACL::STask::VsimSnapshot->list() | ----------------------------------------------------------- VsimSnapshot Examples: my $vsim_ss = NACL::STask::VsimSnapshot->create( command_interface => $host, vsim => $vsim, snapshot => $snapshot_name , ); my @all_snapshots = $vsim_ss->list(); # do some operations $vsim_ss->restore(); $vsim_ss->delete(); =cut =head1 ATTRIBUTES =head2 command_interface A component object that represents the host to which to send vsim commands. =head2 snapshot Name of the Snapshot. =head2 vsim Name/object of the vsim. This field is optional. =head2 vsimid Short identification number for vsim. =head2 vsimuser Owner of the vsim =cut package NACL::STask::VsimSnapshot; use strict; use warnings; use base qw(NACL::STask::STask); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::STask::STask; use Params::Validate qw( validate_with SCALAR OBJECT BOOLEAN UNDEF); use NACL::APISet::Exceptions::ResponseException qw(:try); use NATE::BaseException qw(:try); use NACL::ComponentUtils qw(Dumper); use NACL::Transit; use constant DEFAULT_TIMEOUT => 1800; use Class::MethodMaker [ new => [ '-hash', 'new' ], scalar => [ { -type => 'NACL::C::CommandInterface' }, 'command_interface' ], scalar => 'vsimid', scalar => 'vsimuser', scalar => 'vsim', scalar => 'snapshot', scalar => '_timeout', scalar => '_apiset', ]; =head1 METHODS =head2 create (Class method) my $vsim = NACL::STask::VsimSnapshot->create( command_interface => $host, vsim => $vsim, snapshot => $snapshot_name , ); Create a Snapshot object that can be used to perform vsim snapshot operation on vsim. This task uses command_interface of machine to run vsim command. Either vsim or vsimid param should be defined. =over =item Options =over =item C<< command_interface => $command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of host machine and used for running a vsim commands.. See L =item C<< vsim => $vsimname >> (Optional) if vsimid & vsimuser are defined. Name of the vsim or Node object of vsim. =item C<< vsimid => $id >> (Optional) if vsim is defined. Short identification number of vsim. =item C<< vsimuser => $user >> (Optional) if vsim is defined. owner of the vsim. =item C<< snapshot => $snapshot >> (Optional) The name of the snapshot to be created. Task will create a snapshot with random name if User didn't pass this param. =item << nacltask_verify_create => 1 >> (Optional) Defaults to '1' Do verification of existence of the snapshot. =item C<< nacltask_if_exists => $action >> (Optional) specifies an action to be taken if the snapshot already exists. $action - if set to "die"(default), an exception is raised. - if set to "reuse", no action is performed, and return reference to the corresponding VsimSnapshot object. - if set to "purge", deletes the existing snapshot before creating it. =item C<< nacltask_timeout => $timeout >> (Optional) Controls how long the command will wait before completing. Default is 1200 seconds. =back =over =item Exceptions =over =item C This type of exception is thrown when verification fails for the created vsim snapshot. =item C This type of exception is thrown when none of the options 'vsim' or 'vsimid' is not defined. =back =cut sub create { $Log->enter() if $may_enter; my $self = shift; $Log->debug( "Opts to 'NACL::STask::VsimSnapshot->create()':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts = validate_with( params => \@_, spec => { 'command_interface' => { type => OBJECT, isa => 'NACL::C::CommandInterface' }, 'vsim' => { type => SCALAR | OBJECT, optional => 1 }, 'vsimuser' => { type => SCALAR | UNDEF, optional => 1 }, 'vsimid' => { type => SCALAR, optional => 1 }, 'snapshot' => { type => SCALAR, default => NACL::STask::STask->random_name_generator( prefix => 'snapshot', size => 1 ) }, 'nacltask_verify_create' => { type => BOOLEAN, default => 1 }, 'nacltask_timeout' => { type => SCALAR, default => DEFAULT_TIMEOUT }, 'nacltask_if_exists' => NACL::STask::STask->_if_exists_validate_spec(), }, allow_extra => 1 ); unless ( exists $opts{vsim} || exists $opts{vsimid} ) { $Log->exit() if $may_exit; NATE::BaseException->throw("Either vsim or vsimid must be defined"); } ## end unless ( exists $opts{vsim...}) my $timeout = delete( $opts{'nacltask_timeout'} ); my $nacltask_verify = delete( $opts{nacltask_verify_create} ); my $if_exists = delete( $opts{nacltask_if_exists} ); $self = NACL::STask::VsimSnapshot->new( $self->_hash_move( source => \%opts, move => [ 'vsim', 'vsimid', 'vsimuser', 'command_interface', 'snapshot' ] ) ); $self->_timeout($timeout); my ($vsimid) = $self->_get_vsim_details(); my $snapshot = $self->snapshot(); my $apiset = $self->_get_apiset(); my $redo_flag = 1; REDO: try { $apiset->vsim_snap( vsimid => $vsimid, 'snap-name' => $snapshot, 'connectrec-timeout' => $timeout ); } ## end try catch NACL::APISet::Exceptions::ResponseException with { my $e = shift; $Log->debug("Unable to create $snapshot"); my $op = $e->text(); if ( $op =~ /Snapshot with name $snapshot already exists for VM/i ) { if ( $if_exists eq 'reuse' ) { $Log->exit() if $may_exit; return $self; } elsif ( $if_exists eq 'purge' && $redo_flag ) { $self->delete(); $redo_flag = 0; goto REDO; } } ## end if ( $op =~ ...) $Log->exit() if $may_exit; $e->throw(); }; if ($nacltask_verify) { $self->does_snapshot_exist(); } $Log->exit() if $may_exit; return $self; } ## end sub create =head2 restore (Instance method) $vsim_obj->restore(); or (Class method) NACL::STask::VsimSnapshot->restore( command_interface => $host, vsim => $vsim, snapshot => $snapshot_name ); Restore a vsim to a existing snapshot. If vsim is not up after restore operation, it will throw exception =over =item Options =over =item C<< command_interface => $command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of host machine. See L =item C<< vsim => $vsimname >> (Optional) if vsimid & vsimuser are defined. Name of the vsim or Node object of vsim. Restore operation may cause connection loss, So user has to takecare of refreshing vsim command interface object after this operation. =item C<< vsimid => $id >> (Optional) if vsim is defined. Short identification number of vsim. =item C<< vsimuser => $user >> (Optional) if vsim is defined. owner of the vsim. =item C<< snapshot => $snapshot >> (Required) The name of the snapshot to be restored. =item C << nacltask_boot_after_restore => 1|0 >> # default 1 if '1' (Defaults) Checks for vsim(only ONTAP vsim) is alive or not. If vsim is not alive,than its try to change its state to CLI(via NACL::Transit). if '0' Didn't check for vsim health. =back =over =item Exceptions =over =item C This type of exception is thrown when none of the options 'vsim' or 'vsimid' is not defined. =back =cut sub restore { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $Log->debug( "Opts to 'NACL::STask::VsimSnapshot->restore()':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts; if ( ref($pkg_or_obj) ) { %opts = validate_with( params => \@_, spec => { nacltask_boot_after_restore => { type => BOOLEAN, default => 1 }, }, ); } else { %opts = validate_with( params => \@_, spec => { 'command_interface' => { type => OBJECT, isa => 'NACL::C::CommandInterface' }, 'vsim' => { type => SCALAR | OBJECT, optional => 1 }, 'vsimuser' => { type => SCALAR | UNDEF, optional => 1 }, 'vsimid' => { type => SCALAR, optional => 1 }, 'nacltask_timeout' => { type => SCALAR, default => DEFAULT_TIMEOUT }, 'snapshot' => { type => SCALAR }, 'nacltask_boot_after_restore' => { type => BOOLEAN, default => 1 }, }, ); unless ( exists $opts{vsim} || exists $opts{vsimid} ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Either vsim or vsimid must be defined"); } ## end unless ( exists $opts{vsim...}) $pkg_or_obj = NACL::STask::VsimSnapshot->new( $pkg_or_obj->_hash_move( source => \%opts, move => [ 'vsim', 'vsimid', 'vsimuser', 'command_interface', 'snapshot' ] ) ); } ## end else [ if ( ref($pkg_or_obj) )] my $self = $pkg_or_obj; my $timeout = $opts{nacltask_timeout} || $self->_timeout(); my ( $vsimid, $vsimname ) = $self->_get_vsim_details(); my $snapshot = $self->snapshot(); my $apiset = $self->_get_apiset(); try { $apiset->vsim_snapback( vsimid => $vsimid, 'snap-name' => $snapshot, 'connectrec-timeout' => $timeout ); } ## end try catch NACL::APISet::Exceptions::ResponseException with { my $e = shift; $Log->debug( "Unable to restore $snapshot snapshot for $vsimname vsim"); $Log->exit() if $may_exit; $e->throw(); }; if ( $opts{nacltask_boot_on_restore} ) { if(!defined($vsimname)) { $Log->exit() if $may_exit; NATE::BaseException->throw("unable to figure out the vsimname."); } my $transit_obj = NACL::Transit->new( name => $vsimname ); if ( $transit_obj->get_state() ne 'CLI' ) { $Log->comment( "vsim state changed after restore, we are trying to boot up this vsim" ); $transit_obj->change_state( to => 'CLI', timeout => $timeout ); } ## end if ( $transit_obj->get_state...) } ## end if ( $opts{nacltask_verify_restore...}) $Log->exit() if $may_exit; return $self; } ## end sub restore =head2 delete (Instance method) $vsim_obj->delete(); or (Class method) NACL::STask::VsimSnapshot->delete( command_interface => $host, vsim => $vsim, snapshot => $snapshot_name ); Deletes a existing snapshot. =over =item Options =over =item C<< command_interface => $command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of host machine. See L =item C<< vsim => $vsimname >> (Optional) if vsimid & vsimuser are defined. Name of the vsim or Node object of vsim. =item C<< vsimid => $id >> (Optional) if vsim is defined. Short identification number of vsim. =item C<< vsimuser => $user >> (Optional) if vsim is defined. owner of the vsim. =item C<< snapshot => $snapshot >> (Required) The name of the snapshot to be deleted. =back =over =item Exceptions =over =item C This type of exception is thrown when none of the options 'vsim' or 'vsimid' is not defined. =back =cut sub delete { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $Log->debug( "Opts to 'NACL::STask::VsimSnapshot->delete()':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts; unless(ref($pkg_or_obj) ) { %opts = validate_with( params => \@_, spec => { 'command_interface' => { type => OBJECT, isa => 'NACL::C::CommandInterface' }, 'vsim' => { type => SCALAR | OBJECT, optional => 1 }, 'vsimuser' => { type => SCALAR | UNDEF, optional => 1 }, 'vsimid' => { type => SCALAR, optional => 1 }, 'vsim' => { type => SCALAR | OBJECT }, 'snapshot' => { type => SCALAR }, 'nacltask_timeout' => { type => SCALAR, default => DEFAULT_TIMEOUT }, }, ); unless ( exists $opts{vsim} || exists $opts{vsimid} ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Either vsim or vsimid must be defined"); } ## end unless ( exists $opts{vsim...}) $pkg_or_obj = NACL::STask::VsimSnapshot->new( $pkg_or_obj->_hash_move( source => \%opts, move => [ 'vsim', 'vsimid', 'vsimuser', 'command_interface', 'snapshot' ] ) ); } ## end unless ( ref($pkg_or_obj) ) my $self = $pkg_or_obj; my $snapshot = $self->snapshot(); my $timeout = $opts{nacltask_timeout} || $self->_timeout(); my ($vsimid) = $self->_get_vsim_details(); my $apiset = $self->_get_apiset(); try { $apiset->vsim_snapdelete( vsimid => $vsimid, 'snap-name' => $snapshot, 'connectrec-timeout' => $timeout ); } ## end try catch NACL::APISet::Exceptions::ResponseException with { my $e = shift; $Log->debug("Unable to delete $snapshot snapshot"); $Log->exit() if $may_exit; $e->throw(); }; $Log->exit() if $may_exit; } ## end sub delete =head2 list (Instance method) my @snapshots=$vsim_obj->list(); or (Class method) my $snapshot=NACL::STask::VsimSnapshot->list( command_interface => $host, vsim => $vsim, ); Returns a list of existing snapshot under a vsim. =over =item Options =over =item C<< command_interface => $command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of host machine. See L =item C<< vsim => $vsimname >> (Optional) if vsimid & vsimuser are defined. Name of the vsim or Node object of vsim. =item C<< vsimid => $id >> (Optional) if vsim is defined. Short identification number of vsim. =item C<< vsimuser => $user >> (Optional) if vsim is defined. owner of the vsim. =back =over =item Exceptions =over =item C This type of exception is thrown when none of the options 'vsim' or 'vsimid' is not defined. =back =cut sub list { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $Log->debug( "Opts to 'NACL::STask::VsimSnapshot->list()':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts; unless ( ref($pkg_or_obj) ) { %opts = validate_with( params => \@_, spec => { 'command_interface' => { type => OBJECT, isa => 'NACL::C::CommandInterface' }, 'vsim' => { type => SCALAR | OBJECT, optional => 1 }, 'vsimuser' => { type => SCALAR | UNDEF, optional => 1 }, 'vsimid' => { type => SCALAR, optional => 1 }, 'nacltask_timeout' => { type => SCALAR, default => DEFAULT_TIMEOUT }, }, ); unless ( exists $opts{vsim} || exists $opts{vsimid} ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Either vsim or vsimid must be defined"); } ## end unless ( exists $opts{vsim...}) $pkg_or_obj = NACL::STask::VsimSnapshot->new( $pkg_or_obj->_hash_move( source => \%opts, move => [ 'vsim', 'vsimid', 'vsimuser', 'command_interface', ] ) ); } ## end unless ( ref($pkg_or_obj) ) my $self = $pkg_or_obj; my $snapshot = $self->snapshot(); my $timeout = $opts{nacltask_timeout} || $self->_timeout(); my ($vsimid) = $self->_get_vsim_details(); my $resp_obj; my $apiset = $self->_get_apiset(); try { $resp_obj = $apiset->vsim_snaplist( vsimid => $vsimid, 'connectrec-timeout' => $timeout ); } ## end try catch NACL::APISet::Exceptions::ResponseException with { my $e = shift; $Log->debug("Unable to list snapshot under the vsim"); $Log->exit() if $may_exit; $e->throw(); }; my @snapshots = @{ $resp_obj->get_parsed_output()->[0]->{snapshot} }; $Log->exit() if $may_exit; return wantarray ? @snapshots : $snapshots[0]; } ## end sub list =head2 does_snapshot_exist (Instance method) $vsim_obj->does_snapshot_exist(); or (Class method) NACL::STask::VsimSnapshot->does_snapshot_exist( command_interface => $host, vsim => $vsim, snapshot => $snapshot_name ); Raise an Exception, if snapshot does not exists on vsim. =over =item Options All of the options are same as L<< NACL::STask::VsimSnapshot->create|lib-NACL-STask-VsimSnapshot-pm/create >> =over =back =over =item Exceptions =over =item C This type of exception is thrown when none of the options 'vsim' or 'vsimid' is not defined =back =cut sub does_snapshot_exist { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $Log->debug( "Opts to 'NACL::STask::VsimSnapshot->does_snapshot_exist()':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts; unless( ref($pkg_or_obj) ) { %opts = validate_with( params => \@_, spec => { 'command_interface' => { type => OBJECT, isa => 'NACL::C::CommandInterface' }, 'vsim' => { type => SCALAR | OBJECT, optional => 1 }, 'vsimuser' => { type => SCALAR | UNDEF, optional => 1 }, 'vsimid' => { type => SCALAR, optional => 1 }, 'snapshot' => { type => SCALAR }, 'nacltask_timeout' => { type => SCALAR, default => DEFAULT_TIMEOUT }, }, ); unless ( exists $opts{vsim} || exists $opts{vsimid} ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Either vsim or vsimid must be defined"); } ## end unless ( exists $opts{vsim...}) $pkg_or_obj = NACL::STask::VsimSnapshot->new( $pkg_or_obj->_hash_move( source => \%opts, move => [ 'vsim', 'vsimid', 'vsimuser', 'command_interface', 'snapshot' ] ) ); } ## end unless ( ref($pkg_or_obj) ) my $self = $pkg_or_obj; my $snapshot = $self->snapshot(); my $timeout = $opts{nacltask_timeout} || $self->_timeout(); my ($vsimid) = $self->_get_vsim_details(); my $apiset = $self->_get_apiset(); my $resp_obj; try { $resp_obj = $apiset->vsim_snaplist( vsimid => $vsimid, 'connectrec-timeout' => $timeout ); } ## end try catch NACL::APISet::Exceptions::ResponseException with { my $e = shift; $Log->debug("Unable to list snapshot under the vsim"); $Log->exit() if $may_exit; $e->throw(); }; my @match = grep { $_ eq $snapshot } @{ $resp_obj->get_parsed_output()->[0]->{snapshot} }; if ( $#match < 0 ) { $Log->exit() if $may_exit; NATE::BaseException->throw("$snapshot doesn't exists"); } $Log->exit() if $may_exit; } ## end sub does_snapshot_exist # handler subroutine to extract attributes from vsim sub _get_vsim_details { $Log->enter() if $may_enter; my $self = shift; my $vsim = $self->vsim(); my ($vsim_name) = (); if ( ref($vsim) ) { $vsim_name = $vsim->node(); } else { $vsim_name = $vsim; } if ( !$self->vsimid_isset() ) { #find vsimid from the vsim name if ( $vsim_name =~ /vsim[_-]*(.+)$/ ) { $self->vsimid($1); } else { $Log->exit() if $may_exit; NATE::BaseException->throw( "Unable to figure out the default for vsimid"); } } ## end if ( !$self->vsimid_isset...) if ( !$self->vsimuser_isset() ) { #find vsimid from the vsim name if ( defined($vsim_name) && $vsim_name =~ /(.+?)[_-]vsim/ ) { $self->vsimuser($1); } } ## end if ( !$self->vsimuser_isset...) if ( !defined $vsim_name && defined($self->vsimuser())) { $vsim_name = $self->vsimuser() . '-vsim' . $self->vsimid(); } $Log->debug( "vsimid is " . $self->vsimid() ); $Log->debug( "vimuser is " . $self->vsimuser() ); $Log->debug( "vsim_name is $vsim_name") if(defined $vsim_name); $Log->exit() if $may_exit; return ( $self->vsimid(), $vsim_name ); } ## end sub _get_vsim_details sub _get_apiset { $Log->enter() if $may_enter; my $self=shift; my $apiset; my $ci=$self->command_interface(); my $vsimuser = $self->vsimuser(); if(!$self->_apiset_isset() ) { if( defined($vsimuser) ) { $Log->comment("Creating a new apiset connection and setting USER=$vsimuser"); $apiset = NACL::APISet->new(hostobj=> $ci->hostrec(),category => "Host",interface => "CLI",set => "Linux"); $apiset->execute_raw_command( command => "USER=".$vsimuser ); $apiset->execute_raw_command( command => "echo \$USER" ); }else { $apiset=$ci->apiset(); } $self->_apiset($apiset); } $Log->exit() if $may_exit; return $self->_apiset(); } 1;